Testing in React
1. What to Test and How
We should run tests at different levels of granularity:
- Unit tests
- Integration tests
- 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.
- Arrange: Render the component.
- Act: Any user events or interaction (if applicable for the test).
- 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'}]
[{
}; })