Testing React Component
Render a React Component to Verify Its Behavior
Let’s test TextField
component (at src/components/text-field.jsx
) by renders it and check its content.
Add a file text-field.spec.jsx
with following content:
import * as React from 'react';
import ReactDOM from 'react-dom';
import { TextField } from './text-field';
test(`renders TextField`, () => {
const div = document.createElement('div');
ReactDOM.render(<TextField label="Age" type="number" />, div);
expect(div.querySelector('label').textContent).toBe('Age');
expect(div.querySelector('input').type).toBe('number');
});
Use React Testing Library to Write Maintainable Tests
yarn add -D @testing-library/react
import { render } from '@testing-library/react';
import * as React from 'react';
import { TextField } from './text-field';
test(`renders TextField`, () => {
const { getByLabelText } = render(<TextField label="Age" type="number" />);
expect(getByLabelText('Age').type).toBe('number');
});
-
getByLabelText
is one of the many queries that React Testing Library provides to make DOM assertion easier. -
Note that I no longer assert content of
label
andinput
separately. Instead, by usinggetByLabelText
queries, I’ve implicitly asserted that:"Age"
text is rendered.<label>
tag that containsAge
text is associated to aninput
, ensuring the screen reader can associate them correctly.
Now that we’ve tested the rendering of the component, let’s test the behavior of the component.
Test Behavior of React Component (Event Listener)
The behavior of component that is indicated here is how component responds to event.
Let’s add another test case for your TextField.
import { fireEvent, render } from '@testing-library/react'; // highlight-line
import * as React from 'react';
import { TextField } from './text-field';
test(`renders TextField`, () => {
const { getByLabelText } = render(<TextField label="Age" type="number" />);
expect(getByLabelText('Age').type).toBe('number');
});
// highlight-start
test(`TextField invoke onChangeValue when input value change`, () => {
const onChangeValueHandler = jest.fn();
const { getByLabelText } = render(
<TextField label="Name" onChangeValue={onChangeValueHandler} />
);
fireEvent.change(getByLabelText('Name'), {
target: { value: 'Malcolm' },
});
expect(onChangeValueHandler).toHaveBeenCalledTimes(1);
expect(onChangeValueHandler).toHaveBeenCalledWith('Malcolm');
});
// highlight-end
Exercise
Write tests to verify behaviors of SelectField
.
Test Asynchronous Behavior of React Component
import { render } from '@testing-library/react';
import * as React from 'react';
import { Spinner } from './spinner';
test(`renders without props will show instantly`, () => {
const { getByRole } = render(<Spinner />);
expect(getByRole('progressbar')).not.toBeNull();
});
Now let’s add a test to verify that is delayShow
is provided, nothing will be shown in the beginning.
// ... other tests
test(`renders with delay will show after wait`, () => {
const { getByRole } = render(<Spinner delayShow={200} />);
expect(getByRole('progressbar')).toBeNull();
});
Oops! The test fails!
This is because all getBy*
queries will throw error if not elements match it, which would make debugging easier.
Luckily, there are another set of queries that will not fail if no elements match, which starts with queryBy*
. Let’s replace our getByRole
accordingly:
// ... other tests
test(`renders with delay will show after wait`, () => {
const { queryByRole } = render(<Spinner delayShow={200} />); // highlight-line
expect(queryByRole('progressbar')).toBeNull(); // highlight-line
});
Now that we verify that nothing is shown in the beginning, let’s verify that the spinner will be shown after the delay.
But how do we wait the delay?
Fortunately (again!), there is (yet) another set of queries starts with findBy*
. This set of queries will returns a Promise
once a match is found.
Let’s use it in our test and change our test to an async
function.
// ... other tests
test(`renders with delay will show after wait`, async () => {
// highlight-line
const { queryByRole, findByRole } = render(<Spinner delayShow={200} />); // highlight-line
expect(queryByRole('progressbar')).toBeNull();
// highlight-start
const spinner = await findByRole('progressbar');
expect(spinner).not.toBeNull();
// highlight-end
});
Exercise
Write tests to verify behaviors of ShareButton
.