DEV Community

Cover image for Writing your first Unit Test in React
Sanjeev Sharma
Sanjeev Sharma

Posted on

Writing your first Unit Test in React

Hey ๐Ÿ‘‹

Are you planning to add tests to your React codebase? You cannot find a good tutorial that can help you get started? Then you've landed on the right article. In this article, we'll cover all the steps for writing unit tests. We'll even cover the errors or issues that you might encounter when starting.

This article uses Jest and React Testing Library. It's okay if you want to use other libraries, the fundamentals in this article will help you there as well.

The code is available on GitHub, find the link in the end.


Why should you write tests?

You can develop an entire product without writing tests. Your end users don't care about it. Your Product manager doesn't care about it. Your tester/QA doesn't care about it. But you, as a developer, should! ๐ŸคŒ

Imagine you've a website with thousands of users. You did some refactoring(or added a hotfix) in a common utility function. You tested the change at one place in the app, it worked fine. You deployed it on a "Friday"(rookie mistake). It broke ๐Ÿ”ฅ production over the weekend. The change you made broke the app at few other places. At this point, you wish you had some sort of tests in place that would automatically run before deploying to production.

Above-mentioned scenario is much more common than you think. You might not have faced it(yet!?), but a lot of engineers have, including me.

Few reasons why testing is important:
๐Ÿš€ Helps you ship with confidence.
๐Ÿ“œ Acts like documentation.
๐Ÿ› ๏ธ Helps in debugging and refactoring.
โŒ›๏ธ Reduces development time, not initially, but in the long run.

To all the junior devs looking at a promotion - add testing to your skillset. ๐Ÿ˜‰


The Tutorial

We'll start from scratch, so get your terminals ready. Let's use vite for creating a sample project.

Create Vite app

Once the project is created, run it using the following command.

Running the app

After running, you will see a demo app on the screen.

App running

We won't add new features to this app, but we'll refactor the button into a separate component - so we can write tests for it.

CounterButton component

Let's add two buttons on the screen:

  1. A button that will 2x the count when pressed.
  2. A button that will(in order):
    • Divide by 2 if the count ends with 0.
    • Add 1 if the count is a Fibonacci number.
    • Square the count, otherwise.

App component

We need to declare the functions used, in the utils module. We've used some helper functions which are not needed outside of this module, so we'll not export them.

Utils module

We're done with our setup, let's start with the tests. Instead of jumping directly into React testing, let's write tests for our utils functions first. This will help us in getting a gist of jest in isolation.

Let's write the test for doubleTheNum function.

doubleTheNum test

The code above tests if our function works as expected. Some key components of any test are:

  1. describe function: The first argument is the string that will be displayed when our tests run. The second argument is the actual function that will run the tests. It is used to group similar tests. Currently, it only has 1 test but in another example you'll see it can have multiple tests inside it.

  2. it function: The arguments are similar to describe function. The string argument here should state what does the function in the next argument test as clearly as possible. Alternatively, you can use test function instead of it.

  3. expect statement: The first three lines of the function are straightforward. The last line asserts if our function doubleTheNum ran correctly. It also uses toEqual - a matcher function.

There are a lot of matchers available in jest. For exmaple:

  • toBeNull matches only null.
  • toBeTruthy matches anything that an if statement treats as true.

Read more about matchers here.

To run the test we need jest installed.

Jest installation

Let's also add a script in package.json to run tests.

test script

Finally, run the tests using yarn test.


For most of you the above steps should be enough. If you face any issues related to module imports or TypeScript. Follow these steps.

  1. Install and set up @babel/preset-env.

Installing babel preset

Add it to package.json

Babel config

  1. Install packages for TypeScript support.

Install ts packages

Add jest configuration in jest.config.ts.

jest config

Run the tests again, it should work this time.


Test output

In the output, you can see the strings we used in describe and it function.

๐ŸŽ‰ Congratulations on writing your first test!


Enjoying the article so far? Check out my most popular article on Redux: Just Redux: The Complete Guide with ~25K reads.

Need a break? Checkout this amazing picture from my most recent trip to Rishikesh.

Rishikesh

More on my Instagram. ๐Ÿ˜‰


Let's write the test for our funkyNum function now.

funkyNum tests

When writing tests try to cover most of the branches and statements of a function. Better coverage gives more confidence.

If you run the tests again, you should see the following output.

Tests output

Ideally, we should write a separate describe block for isFibonacci and isPerfectSquare functions. In unit tests, we test code in isolation. For brevity, we didn't do it.

๐Ÿ’ก Quick tips

  • You can skip any test by calling it.skip or test.skip. describe.skip will skip the entire block.

Skip test

  • You can run a single test by calling it.only or test.only.

Only test


We've covered how to test JS code using jest. Let's dive into React testing, finally. ๐Ÿ’ช

We'll need a few packages. Let's install them.

RTL installation

We'll also have to add the environment in jest.config.ts.

jest test env

Now, we'll write the most basic test for CounterButton component.

CounterButton test

We provided the required props and tried to render the component. This should be the first test you write for any component. If it cannot render, it's of no use.

The render function from RTL renders the provided component in document.body.
It also returns some query methods like getByText that can be used to find elements in the DOM.

List of all the query methods is available here.

If you run the tests again, you should see 2 suites - all green and passing.

RTL tests output

The second test we'll write will test the component against the props. You should test for each prop separately, if they are independent.

Props test

The getByText is a query method that helps us grab an element by using a string.

The toBeInTheDocument method is matcher just like toEqual. It doesn't come with jest by default. It comes from the package we installed earlier - @testing-library/jest-dom.

There are different packages for different environments like @testing-library/jest-native for React Native.

If you run the test again, it should be passing.

Finally, we have come to the final test of this article and it's an important one. We'll write a test to check if the click handler works as expected.

To generate user events like clicking and typing, we'll need another package.

user events installation

It looks almost same, with some minor differences.

User event test

Notice how the function is now async because of the user event.

On the very first line, jest.fn() is a mock function that tracks a lot of things useful in testing like the number of times it was called, the arguments it was called with, etc. You'll see a lot of these out there.

We've also used a new query method getByRole to find the button element.

We wait for the click event to occur before checking if our mock function was called.

That's it! If you run the tests, they should pass.

Final tests

๐Ÿ”— You can find all the code here.


๐Ÿ‘€ What's next?

If you were able to follow through the article, you can start writing tests in your codebase and explore further.

Some keys topics I'd suggest after this are:

  1. getByTestId - This is a common query method that you'll see out there. When nothing works, this will.
  2. Learn about Setup and Teardown methods. It will level up your testing game.
  3. Learn how to mock npm modules, API calls, global state, context, etc.

If you liked the article, consider sharing it with others. ๐Ÿค

I write detailed articles on such topics, feel free to connect with me on LinkedIn or X. ๐Ÿ™

Top comments (8)

Collapse
 
xuxuewen profile image
xuewen

Hi , Sanjeev Sharma
I am the editor of InfoQ China which focuses on software development. We like your articles and plan to translate one of them entitled โ€œWriting your first Unit Test in React".
Before we translate it into Chinese and publish it on our website, I want to ask for your permission first! This translation version is provided for informational purposes only, and will not be used for any commercial purpose.
In exchange, we will put the English title and link at the end of Chinese article. If our readers want to read more about this, he/she can click back to your website.
Thanks a lot, hope to get your help. Any more question, please let me know.

Collapse
 
thesanjeevsharma profile image
Sanjeev Sharma

Hello ่ฎธๅญฆๆ–‡,

Sure. Go ahead. Please share the article link here once it's published. :)

Collapse
 
xuxuewen profile image
xuewen

I apologize for the late response. Below is the link to the translation. Thank you once again for your authorization.

infoq.cn/article/1ypo54cMidb7PzRpxowD

Collapse
 
vishakhaawasthi01 profile image
VishakhaAwasthi01

very informative ๐Ÿ‘

Collapse
 
thesanjeevsharma profile image
Sanjeev Sharma

glad you liked it :)

Collapse
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix

This is not unit testing. This maybe confusing, since the test runner is the same.
The moment you start to render anything this is component testing and not more/mere unit testing.

The difference:

  • unit testing is fast and should use only use/test pure functions
  • component testing is slow, since everything has to rendered in a jsdom
Collapse
 
thesanjeevsharma profile image
Sanjeev Sharma

Interesting take.

"A software development process in which the smallest testable parts of an application, called units, are individually scrutinized for proper operation."

I see Unit Testing by this definition.

And even on google search, all results on React testing talk about the same stuff I mentioned.

I think "Running time" of a test doesn't make it a unit test. Sure, they should be fast but that's not a compulsion.

Collapse
 
adaptive-shield-matrix profile image
Adaptive Shield Matrix • Edited

Testing static state/props in a components is obviously not very helpful.
Example: to test if a components includes a text "Count is: " is not really useful, since you just "cement" or "pin down" the code and makes you to have to modify code at 2 places at once in each future changes / implementation of new features.

If you test your components, then you obviously want to test some of its logic and/or state transition. Writing your logic inside components instead of writing them in separate functions is obviously a bad choice (if it isn't completely trivial). If you separate your logic, then it is the smallest testable part of the application. Hence unit tests exclude component (rendering) tests.

Running time -> is for increased developer experience. I hope you care for your developer experience?
Have you worked at a big corp, where tests take 30min - 4h?
I can spare you the experience and say that it sucks completely, especially if you touch things that are imported and used in 300+ places at once.

"all results on React testing talk" - Its because of the wrong terminology.
Component-rendering tests can't be the smallest unit, because you do not want to test the rendering. Testing rendering makes only sense if you participate in and advance the development of React itself.