course illustration

Test React Components with Jest and React Testing Library

lessons icon42 video lessonsduration icon2h 24m of learning material

If you want to ship your applications with confidence—and of course you do—you need an excellent suite of automated tests to make absolutely sure that when changes reach your users, nothing gets broken. To get this confidence, your tests need to realistically mimic how users actually use your React components. Otherwise, tests could pass when the application is broken in the real world.

In this course, we’ll write a series of render methods and run a range of tests to see how we can get the confidence we’re looking for, without giving up maintainability or test run-speed.

Lessons

1. What's changed in Test React Components with Jest and React Testing Library

duration icon 5m

2. Intro to Test React Components with Jest and React Testing Library

duration icon 1m

3. Render a React Component for Testing

Let’s start basic by using ReactDOM to render a simple React component to a <div> we create ourselves and assert that it’s rendering the right thing based on the props we provide.

duration icon 2m

4. Use Jest DOM for Improved Assertions

The Jest DOM library provides really useful extensions to jest’s built-in assertion library that will make it easier for us to write our test assertions (like toHaveTextContent). Let’s see how to use that in our project.

duration icon 3m

5. Use DOM Testing Library to Write More Maintainable React Tests

Our tests are currently tightly coupled to the implementation of our component’s element structure. A refactor to that structure wont break our application, but will definitely break our tests because we’re testing implementation details. Let’s use DOM Testing Library to create a custom render function that will give us some helpful utilities for searching for elements in the DOM in the same way a user would.

duration icon 4m

6. Use React Testing Library to Render and Test React Components

Let’s create a simple render method to be able to reuse this functionality for our other tests. The render method we’ve created is similar to the render method that’s provided by React Testing Library. Let’s swap our implementation to that!

duration icon 1m

7. Debug the DOM State During Tests using React Testing Library’s debug Function

While you’re writing your tests it can be helpful to see what the DOM looks like. You can do this with React Testing Library’s debug function which will log a formatted and syntax highlighted state of the DOM at the time it is called.

duration icon 1m

8. Test React Component Event Handlers with fireEvent from React Testing Library

The fireEvent utility in React Testing Library supports all the events that you regularly use in the web (change, click, etc.). Let’s see how we can test our change event handler with an input.

duration icon 3m

9. Improve Test Confidence with the User Event Module

The User Event module is part of the Testing Library family of tools and lets you fire events on DOM nodes that more closely resemble the way your users will interact with your elements. Let’s refactor our fire event usages to use that instead.

duration icon 1m

10. Test Prop Updates with React Testing Library

Sometimes it can be useful to change the props of a component you’ve rendered and make assertions about what’s rendered after that prop change has taken place. Let’s see how to go about doing this with React Testing Library.

duration icon 1m

11. Assert That Something is NOT Rendered with React Testing Library

It’s pretty straightforward to assert that a certain element is rendered with react-testing-library, but what if we want to ensure that something is NOT being rendered. For example, if we re-render our component with a different maximum amount leading to the error message being hidden. Let’s see how we can use the query* APIs from react-testing-library to assert that certain elements are not rendered.

duration icon 2m

12. Test Accessibility of Rendered React Components with jest-axe

While not all of accessibility testing of a web application can be automated, much of it can be and quite easily using axe-core and jest-axe. Let’s see how to integrate this with React Testing Library to help catch common accessibility issues.

duration icon 3m

13. Mock HTTP Requests with jest.mock in React Component Tests

If you have a component that makes HTTP requests, you’ll probably want to mock those out for UI unit and integration tests. Let’s see how to use jest’s built-in mocking capabilities to do that.

duration icon 6m

14. Mock HTTP Requests with MSW

Mocking an API module works, but it's incomplete because we have to make all these assertions that we're calling it properly and then we'd need to write some other tests for the API module itself to make sure that it responds properly to those calls.

In this lesson, I'll show you how you can use a package called MSW to intercept and handle those HTTP requests instead so we can get a great deal more confidence.

Get the code on GitHub

duration icon 6m

15. Mock react-transition-group in React Component Tests with jest.mock

There are some situations where you want to focus your tests on a particular component and not any of its children. There are other situations where you want to mock out the behavior of components that your component renders. In the case of react-transition-group, we don’t want to have to wait until the transition has completed before we can go on with our tests. Let’s see how we can mock the implementation of react-transition-group using jest.mock to make our tests more reliable and easier to write and maintain.

duration icon 4m

16. Test componentDidCatch Handler Error Boundaries with React Testing Library

If the user experiences an error while using your application you may want to send information to a monitoring tool to ensure you can be made aware of the error and deal with it as quickly as possible. Let’s see about testing a component that is responsible for this componentDidCatch error boundary using React Testing Library.

duration icon 6m

17. Hide console.error Logs when Testing Error Boundaries with jest.spyOn

When testing an error boundary, your console will be filled with console.error calls from React. Those can be a real distraction from the rest of the output for your tests. Let’s clean those up with jest.spyOn.

duration icon 2m

18. Ensure Error Boundaries Can Successfully Recover from Errors

Our error boundary has some other use cases that it supports and we should try to make sure our tests cover all those use cases, so let’s add a test to make sure the recovery feature of our error boundary works properly.

duration icon 3m

19. Use React Testing Library’s Wrapper Option to Simplify using rerender

We have a bit of repetition in our rerender calls here and it would be nice if we could avoid that. Let’s use the wrapper option for React Testing Library so we can avoid the repetition in our rerender calls.

duration icon 1m

20. Test Drive the Development of a React Form with React Testing Library

Normally using Test Driven Development (TDD) with UI is really difficult because testing utilities for UI often tie your tests closely to the implementation. Because React Testing Library is not this way, we can actually use it to TDD our UI. Let’s build the structure of a form using TDD with React Testing Library.

duration icon 3m

21. Test Drive the Submission of a React Form with React Testing Library

Now that we have our react form structure written using TDD, let’s take it a step further and TDD our form’s submission with an onSubmit handler as well.

duration icon 1m

22. Test Drive Mocking react-router’s Redirect Component on a Form Submission

Once the data has been saved on the server, we’ll want to redirect the user to the home screen with react-router’s <Redirect /> component. Let’s go ahead and mock that component as well and verify that it’s being rendered with the correct props. We’ll have to make our test asynchronous so we can make that assertion because the <Redirect /> component is rendered after the async call happens.

duration icon 5m

23. Test Drive the API Call of a React Form with React Testing Library

It’s great that our form can be submitted, but we need to get that data to our server to actually save the post to the database. We don’t want to actually make requests to the server during these unit tests, so we’ll mock out the function responsible for doing that and assert that it’s being called with the right data. Then we’ll go ahead and implement that functionality in our form component.

duration icon 6m

24. Test Drive Assertions with Dates in React

Our post should probably have a creation date associated with it (typically you’ll add this on the server, but for the purposes of our example, we’ll do it on the client). Dates are notoriously challenging to test. There are existing libraries that help, but often you can get close enough to the desired effect for your assertions. Let’s go ahead and test that a correct creation date has been set for our posts.

duration icon 3m

25. Use Generated Data in Tests with tests-data-bot to Improve Test Maintainability

A really important aspect of TDD is the refactor phase. A critical piece to making your tests easier to maintain is using code structure and values to communicate what is important and what is not. We’ll use data generation with test-data-bot to communicate that the specific data values are irrelevant and it’s just what kind of data it is. Let’s refactor our tests to communicate this effectively.

duration icon 3m

26. Test Drive Error State with React Testing Library

We have the happy path covered for our post editor component, but what happens if there’s an error in saving the user’s information? We should probably show them an error message and give them the chance to try again. Let’s add a new test for this error case and implement some error handling.

duration icon 5m

27. Write a Custom Render Function to Share Code between Tests and Simplify Tests

We’ve finished with our post editor component, but let’s not forget the refactor phase! We have a bunch of duplicate logic between our two tests. When you have multiple tests with duplicate logic, it becomes hard for folks who come to maintain the tests later to identify what the differences between the tests are. This makes it harder to understand what the different features of the component are. So let’s refactor the tests slightly so we can communicate clearly what the important and unique parts of the tests are to make it easier for future maintainers.

duration icon 2m

28. Test React Components that Use the react-router Router Provider with createMemoryHistory

Mocking the <Redirect /> component in react-router works, but it’s imperfect because we don’t know for sure that the user will be redirected properly. Alternatively, we can render our component within a Router with a custom implementation of a history via createMemoryHistory. Then we can make assertions on that history object.

duration icon 3m

29. Initialize the `history` Object with a Bad Entry to Test the react-router no-match Route

If the user enters a bad URL or there’s some kind of routing error, we should show the user a helpful error page. Let’s see how we can test that our page is wired up with the router properly to do this.

duration icon 1m

30. Create a Custom render Function to Simplify Tests of react-router Components

Many of our components need (or will eventually need) the router context from the router provider. Let’s write a custom render method that looks and acts like the render method from React Testing Library but adds features for our router so we don’t have to concern ourselves about the implementation detail of the router.

duration icon 5m

31. Test a Redux Connected React Component

Similar to react-router, we can render our component under test with the redux provider, our full redux store, and then we can interact with our component in the same way the user would and be confident that our component, action creators, and reducers are all working properly.

duration icon 3m

32. Test a Redux Connected React Component with Initialized State

There are definitely some scenarios where you want to test what a component will do given certain state in the store. Let’s see how we can initialize our redux provider with a store that has some custom initialized data. One thing that I should note here is that while it can be useful to do this, make sure you at least have one test that verifies that you can into this state in the first place.

duration icon 1m

33. Create a Custom Render Function to Simplify Tests of Redux Components

It’s very common for our components to use state from the redux store (or to be refactored to use it eventually). Having to change our tests every time we do this would be frustrating. Let’s see how we can write a render method we could use to not worry about that implementation detail when it’s irrelevant for our tests. This could be combined with the custom render method for the router to be a single render method that supports all providers our components need.

duration icon 4m

34. Test a Custom React Hook with React’s Act Utility and a Test Component

We’ve got a custom useCounter hook here and we want to make sure the increment and decrement functions it returns will update the count state that it returns. Because hooks must be run within the render phase of a component, we’ll create a simple component that simply uses the hook and render that component. One catch is that whenever there’s a state update in your tests, React needs you to wrap that in a call to act. We don’t have to do that for regular component tests that use React Testing Library utilities because those utilities manage the act call internally, but since we’re calling these state updater functions ourselves, we have to wrap things in act. Let’s do that for our useCounter hook.

duration icon 3m

35. Write a Setup Function to Reduce Duplication of Testing Custom React Hooks

Once we add tests for all the use cases of our hook, we’ll notice a bunch of common setup, so let’s put that in a setup function.

duration icon 3m

36. Test a Custom React Hook with renderHook from React Hooks Testing Library

That setup function is pretty handy. Seems like a good opportunity for an abstraction. Well, we already have one! It’s called React Hooks Testing Library. Let’s swap our setup function for the renderHook function from @testing-library/react-hooks.

duration icon 1m

37. Test Updates to Your Custom React Hook with rerender from React Hooks Testing Library

Just like React Testing Library, React Hooks Testing Library has a rerender function which can be useful for testing changes to props. Let’s test what happens if the step option changes for our hook.

duration icon

38. Test React Portals with within from React Testing Library

When you use a React Portal, much of your component can be rendered outside the main DOM tree. Let’s see how we can use utilities provided by React Testing Library to allow us to select elements within the portal. To perform actions and assert on what’s rendered.

duration icon 4m

39. Test Unmounting a React Component with React Testing Library

Sometimes your react component may need to do some cleanup work in the return value from useEffect or useLayoutEffect, or the componentWillUnmount lifecycle method for class components. Luckily for us, as far as React Testing Library is concerned, whichever of those you use is an implementation detail, so your test looks exactly the same regardless of how you implement it. Check out how you can test that simply with React Testing Library and a <Countdown /> component.

duration icon 4m

40. Write React Application Integration Tests with React Testing Library

You can get a huge amount of confidence and coverage from integration tests that test an entire page, or even your entire app. Let’s write a test that renders our whole app using React Testing Library and navigate around it like a normal user would. These tests are typically a bit longer, but they provide a huge amount of value.

duration icon 7m

41. Improve Reliability of Integration Tests using find* Queries from React Testing Library

By using some of the get queries, we’re assuming that those elements will be available on the page right when we execute the query. This is a bit of an implementation detail and it’d be cool if we could not make that assumption in our test. Let’s swap all those for find queries.

duration icon 2m

42. Improve Reliability of Integration Tests Using the User Event Module

Let’s take things a step closer to the way our app is used by using the User Event module for interacting with our app. Once we’ve done all this implementation detail-free testing, we’ll find out that we can drastically change the implementation of our app and our tests can give us confidence that our changes were successful. That’s fantastic!

duration icon 1m