I have some work in progress tests, this is working as expected:
describe("Parent", () => {
afterEach(() => {
cleanup();
jest.resetModules();
});
describe("Test 1", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
test("1 ", () => {
expect(wrapper.baseElement).toMatchSnapshot();
expect(wrapper.getByText("Apply").disabled).toBe(true);
});
});
describe("Test 2", () => {
test("1 ", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
console.log(wrapper.getByText("Apply").disabled);
expect(1).toBe(1);
});
});
});
However when I move the 2nd render function out of the test it errors:
describe("Parent", () => {
afterEach(() => {
cleanup();
jest.resetModules();
});
describe("Test 1", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
test("1 ", () => {
expect(wrapper.baseElement).toMatchSnapshot();
expect(wrapper.getByText("Apply").disabled).toBe(true);
});
});
describe("Test 2", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
test("1 ", () => {
console.log(wrapper.getByText("Apply").disabled);
expect(1).toBe(1);
});
});
});
The error Im getting is
Found multiple elements with the text: Apply
I can see in the console that the component is being rendered twice, so I think the cleanup function must not be working properly in regards to the describe block. This is weird as we already have Enzyme tests and the setup and tear down works fine for those.
React Testing Library is not specific to any testing framework; we can use it with any other testing library, although Jest is recommended and preferred by many developers. create-react-app uses both Jest and React Testing Library by default.
React Testing Library is a testing utility tool that's built to test the actual DOM tree rendered by React on the browser. The goal of the library is to help you write tests that resemble how a user would use your application.
To find elements by className in React testing library: Render a component and destructure the container object from the result. Use the getElementsByClassName() method on the container to find elements by class name.
To understand this we need to understand a little bit about how Jest
runs our tests and how React Testing Library
renders our components.
Consider the code below and try to guess what the output will be:
describe('First describe', () => {
console.log('First describe');
it('First test', () => {
console.log('First test');
});
});
describe('Second describe', () => {
console.log('Second describe');
it('Second test', () => {
console.log('Second test');
});
});
Output (hover to see):
First describe
Second describe
First test
Second test
Note that all describe
methods were initialised before the tests started to run.
This should already give you an idea of the problem, but let's now look at RTL.
Consider the code below and try to guess what the DOM will look like in the console:
const Greeting = () => 'Hello world';
describe('First describe', () => {
const wrapper = render(<Greeting />);
it('First test', () => {
console.log(wrapper.debug());
});
});
describe('Second describe', () => {
render(<Greeting />);
});
Output (hover to see):
<body>
<div>Hello world</div>
<div>Hello world</div>
</body>
When we don't specify a base element to the render
function, it always uses the same document.body
The <div>
elements wrapping Hello world
are added by RTL when we don't specify a custom container.
All RTL queries are bound to the base element by default--document.body
in this case.
Therefore,
getByText('Hello world'); // will find two elements and throw
This is how the code in RTL looks like for the render
function. (semi-pseudo code)
if(!baseElement) {
baseElement = document.body // body will be shared across renders
}
if(!container) {
baseElement.appendChild(document.createElement('div')) // wraps our component
}
ReactDOM.render(component, container)
return { container, baseElement, ...getQueriesForElement(baseElement) }
Do one of the following:
render
inside the it
or test
methodscontainer
in the queriesrender
I was running into a separate problem, but in the midst found the likely answer to your problem.
Jest describe
blocks run in sequence before any of the tests run. Source
So you should basically never execute code in a describe block aside from variable scoping. If you need to set a variable or render a mock that is used across multiple tests, put it in a lifecycle method like beforeAll
or beforeEach
.
Another Quick fix for this could be wrapping your component with "React.Fragment" before passing it to render()
test ('should find text in <Component />', () => {
const {getByText} = render (<><Component /></>)
const divElement = getByText (/Find Text/i)
expect (divElement).toBeInTheDocument ()
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With