- #1: Introduction
- #2: Setting up Next.js Project for testing
- #3: Writing the tests
- #4: Are my tests enough?
In the early stages of my career, I can recall dreading writing tests and one major reason was that there was no defined outline of what exactly to test in an application. I know that many people face this issue as well and thats why they do not write tests. However after a lot of research (and writing tests myself) I have discovered from the "greats" that the answer to the question "what should I test?" is really basic.
Maybe if we all know exactly what to test in an application we would be more receptive towards writing tests. Well, the answer to this question is... drum roll please 🥁
We test how a user makes use of our application
It really is that simple. Every application has the intended user experience for which it was built, and your tests should be geared towards making sure that the user goes through your intended experience seamlessly. This is what is called "happy flow/happy path" of an application. In more simple terms, if clicking a button should lead somewhere, then you should test that. If a loader is meant to be showing while a user waits for a resource, you should test that. If a user is meant to see a particular amount of resources, you should test that. You should test how your app is meant to work when everything acts perfectly and sometimes - depending on a couple of factors - also how your app ought to work when everything DOES NOT act perfectly (i.e when errors occur).
Now, I think I can end the article right now, because the question has already been answered right ?😂
On a second note, how about I support that answer with an example to drive the point home? 🤔, sounds good.
Don't worry, this wouldn't be like that difficult teacher's classroom, where you are given a simple example in class and then a difficult take home assignment 😂. The example given here would be as simple as possible yet would mirror an enterprise grade application so you can understand the approach to testing any application.
For this article I would be making use of our default configuration in our Previous article. Check out that article to set up your next.js app for testing.
The User Experience of our application (i.e what the user does) a.k.a USER STORIES
We have established that we want to test exactly how a user uses our application, so the first thing we need to do is to actually make a list of what is expected of our application when used by a user. After that, we can then write tests to fulfill our set expectations.
The application we would use for this article is a post feed, view live site here. I'm sure you are familiar with post feeds because they exist on most social platforms like Twitter, Facebook, Instagram, Reddit e.t.c. We have a list of posts, users can create posts, users can delete post e.t.c. Basically, we would be able to perform the CRUD (Create, Read, Update, Delete) operations in our application.
Unit tests vs integration tests. Which is more important?
Now that we have an application and we know that we need to test how a user uses our app, the next question would be which tests to write first. When it comes to testing frontend applications, some of the commonly known tests include:
- Snapshot Testing
- Unit Testing
- Integration Testing
- End to End Testing
There are many more, however I believe these four are the most popular. Just like in any area of life, among a certain group of things there must be one that stands out as most important, same goes for Testing. The crown for the position of the most important tests is often known to be debated between UNIT TESTING and INTEGRATION TESTING. While some may say Unit Tests are the most important, I am of the school of thought that Integration Tests are far more important.
Unit test is focused on testing just a particular unit/component isolated from the rest of your application (e.g testing a button). However, when a user engages your app, they do not use your units/components in isolation, but rather they use all those units tied up together (e.g they expect a click on a button to make something else happen).
It is very possible for all the units of your application to work well independently, yet not work as expected if not tied properly to each other (e.g if the onClick event on a button is not tied to the callback it's expected to be tied to, maybe you have two buttons with labels "send" and "cancel" and the button that has the text "Send" was mistakenly tied to the callback responsible for "cancel" and not the actual "send" callback ).
However its impossible for your app to work well, and your units not work well (i.e If the "send" button calls the callback responsible for send, then the button is working perfectly). I hope you get that? Its like saying, for a building to stand properly, it definitely means the units that make up that building work properly.
Every time you write an Integration Test, you are indirectly already checking that the Units that make up the integration work perfectly
Let me say this with emphasis, I am not debunking the fact that UNIT tests are essentially very important, what I am saying is that if I were pressed for time and really needed to write "good" tests for my application as quickly as possible, my go to would first be Integration tests since it essentially tests that my app in its entirety works perfectly
Create your user stories
After successfully pushing the idea that Integration Tests (in my opinion) should be considered priority, the first thing we need to do is to create a couple of user stories to know what we want to test
User Story 1: When user first opens our application
- User can see a "Post Feed" text which is the title of our application
- User can see a "textarea" field
- User can see a "loading" text
- After a while "loading" text disappears and user can now see a list of posts
User story 2: User can create a post
- The green post button in the textfield is disabled by default
- User types into the textfield and sees their typed text being displayed
- Immediately the textfield is not empty the post button becomes enabled
- User clicks on the post button, after which something seems to happen, since the text on the green button has changed from "post" to "posting" and is now disabled
- After a while, textfield becomes empty, the text on the button changes back to "post" and the amount of posts have increased by one, with our recently created post at the top of the displayed posts
User story 3: User can delete a post
- User clicks on delete in one post
- The delete and edit button become disabled, while the text on the delete button changes to "deleting..."
- After a while, that particular post disappears from the document and the total amount of posts displayed decreases by one
User story 4: User can edit a post
- User clicks on edit button in one post
- Post details become editable and the buttons change
- User can type into the textfield
- When user is through editing, user clicks submit and the post details become edited
The four user stories above outline the four basic happy user experiences we expect our users to be able to perform. Once we can ascertain that these user stories work as expected, it is safe to say we have built sufficient tests for our application (more on sufficient tests in the article about test coverage, coming up later).
Analysing our application
Now, to further emphasise why integration tests can be more important than unit tests, lets analyse the units in our application. In our application here are the basic units that exist:
- Post card (Complex unit)
That's pretty much it. It would still be great to write unit tests for these units/components, however out of the box we can clearly see that, if we can successfully write integration tests to pass our four user stories above, then we would have indirectly also tested our units, because our integration tests make use of all the units above.
This principle can be used in any application for any feature. It could be testing that a user adds an item to a cart and proceeds to checkout successfully in an e-commerce site, or a user can edit details in their profile as expected. I'm sure you can see that even without checking if our tests are enough, we know we are already checking the important things in our application
In our next article we would take a look at the code responsible for our application and write the actual tests to fulfil the user stories that we have created. See you there