This is my test file:
import List from '../List';
import { render, screen } from '@testing-library/react';
let data = ['one', 'two', 'three'];
describe('Component: List', () => {
let { container } = render(<List items={data} />);
test('(1) list length', () => {
let li = container.querySelectorAll('li');
expect(li.length).toBe(3);
});
test('(2) values in list', () => {
expect(screen.getByText('one')).toBeInTheDocument();
expect(screen.getByText('two')).toBeInTheDocument();
expect(screen.getByText('three')).toBeInTheDocument();
});
});
and I wrote this component:
const List = ({ items }) => {
return (
<ul>
{
items.map((item, index) => <li key={index}>{item}</li>)
}
</ul>
)
}
export default List;
My tests don't pass, I get this error for the (2) values in list
:
TestingLibraryElementError: Unable to find an element with the text: one. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
In the event that, test (2) passes when I comment or remove test (1) :
describe('Component: List', () => {
let { container } = render(<List items={data} />);
// test('(1) list length', () => {
// let li = container.querySelectorAll('li');
// expect(li.length).toBe(3);
// });
test('(2) values in list', () => {
expect(screen.getByText('one')).toBeInTheDocument();
expect(screen.getByText('two')).toBeInTheDocument();
expect(screen.getByText('three')).toBeInTheDocument();
});
});
Does container
effect on other selectors?
Because RTL will unmount React trees that were mounted with render
after each test. This is called cleanup.
Please note that this is done automatically if the testing framework you're using supports the
afterEach
global and it is injected to your testing environment (like mocha, Jest, and Jasmine). If not, you will need to do manual cleanups after each test.
You only render the List
component once in describe
block. When the first test is finished, RTL will unmount the List
component, there is nothing in "screen", that's why your second test failed.
There are two ways to solve this:
To make this even easier, you can also simply import @testing-library/react/dont-cleanup-after-each which
will do the same thing. Just make sure you do this before importing @testing-library/react
. You could do this with Jest's setupFiles
configuration:
If you are using jestjs as your testing framework
{
// ... other jest config
setupFiles: ['@testing-library/react/dont-cleanup-after-each']
}
But multiple test cases use the same component, and the order in which the tests are executed is out of order, which can cause tests to interact with each other.
Simply put: Test cases are not isolated from each other and rely on the same test data.
2. Create fresh test data and test double for each test case, isolate tests.
import List from './';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
let data = ['one', 'two', 'three'];
describe('Component: List', () => {
// let { container } = render(<List items={data} />);
test('(1) list length', () => {
let { container } = render(<List items={data} />);
let li = container.querySelectorAll('li');
expect(li.length).toBe(3);
});
test('(2) values in list', () => {
render(<List items={data} />);
expect(screen.getByText('one')).toBeInTheDocument();
expect(screen.getByText('two')).toBeInTheDocument();
expect(screen.getByText('three')).toBeInTheDocument();
});
});
Or, render
the component in beforeEach()
hook.
update: How does RTL cleanup work?
And reply to your comment: does cleanup depend on each test() or it() blocks?
Every time you mount the component in the container with render
function, the mountedContainers set will add the component container.
mountedContainers set defined in module scope.
After each test, the cleanup function will unmount the component from the container, remove the container from the document, and remove the container from the mountedContainers
set.
So you render the component in the describe
function body, the describe function only executes once during the whole test. mountedContainers
add your component once, When the first test case is performed, mountedContainers
set will remove it. Then there is nothing in the "screen", the second test fails.