React: Testing

Part 6: Testing my patience
Engineering
Software
React
WebDev
Author

Gurpreet Johl

Published

March 20, 2024

Testing in React

1. What to Test and How

We should run tests at different levels of granularity:

  1. Unit tests
  2. Integration tests
  3. End-to-end tests

We need some build tools to help us run tests.

Jest and React Testing Library are useful for unit and integration tests, which are the main focus of this page. End-to-end testing can be done with tools like Selenium or Cypress.

1.1. Jest

Jest is a testing framework that allows us to run JavaScript tests.

The test function creates a unit test. It takes a name of the test and an anonymous function which runs the test code.

The expect function then defines some behaviour to assert. For pure JavaScript util functions, this is all we need.

For React components, we need to test the rendered component with the help of React Testing Library.

1.2. React Testing Library

React testing library is a library that lets us simulate rendered components and assert characteristics of them.

The render function is used to simulate the dom to render a component in a test. The screen function is used to get properties from the simulated DOM, eg screen.getByText to assert a particular passage of text appears on the screen.

The get functions return an error if the object does not exist. The query functions return null instead. The latter is useful if we want to test when something should NOT be rendered.

The getByRole function is useful to pick out specific elements. See available roles here.

The userEvent object from React Testing Library simulates user actions like click or hover, so we can test interactive behaviour of components.

2. The 3 As of Testing

The general pattern for testing is: Arrange, Act, Assert.

  1. Arrange: Render the component.
  2. Act: Any user events or interaction (if applicable for the test).
  3. Assert: Check the desired output is in the DOM.

3. Test Suites

We may want to group tests for related features/components together for readability.

Use the describe function to define a test suite.

It takes a test suite description string and anonymous function as argumens. Then each of the tests are defined within it.

4. Testing Asynchronous Code

The get and query functions attempt to retrieve elements from the DOM immediately, as soon as the component is rendered. This is not the behaviour we want for components with asynchronous elements which may take time to fetch data.

The findByRole function, and related “find” functions, return a promise which will wait and re-attempt to find the element before failing. This allows us to test async code. You can pass optional args to the find functions to set how long they should wait, when to retry etc.

We generally want to avoid sending external requests as part of unit tests, so this is more relevant to integration or end-to-end tests.

For unit tests, we can mock the results.

5. Using Mocks

We want to mock out external calls like fetch when we run them is our unit tests.

We can overwrite the window.fetch method:

window.fetch = jest.fn();
window.fetch.mockResolvedValueOnce({
    json: async() => {
        // The mock values
        [{id: 1, text: 'example text goes here'}] 
    }
});

References

Back to top