Home > database >  react-testing-library doesn't find element in this case
react-testing-library doesn't find element in this case

Time:03-16

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?

CodePudding user response:

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:

1. Skipping Auto Cleanup

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.

  • Related