Библиотека тестирования React Тесты маршрутизатора не проходят, если следовать собственному руководству RTL

Я пытаюсь провести простой тест в простом приложении для навигации с помощью библиотеки тестирования React. В настоящее время я пытаюсь использовать пример метода, показанный в собственном руководстве RTL, однако это, похоже, не работает.

App.js

return (
    <BrowserRouter>
      <div>
        <Route exact path="/" component={Home} />
        <Route exact path="/hello" component={Hello} />
      </div>
    </BrowserRouter>
  );

Home.jsx

return (
    <div data-testid="home-container">
      <Link to="/hello">Link to hello</Link>
    </div>
  );

Hello.jsx

return (
    <div data-testid="hello-container">
      <Link to="/">&lt; Back</Link>
      <h1 data-testid="hello-title">Hello component</h1>
    </div>
  );

Протестируйте Home.jsx в Home.spec.js

test("Navigate to hello page when clicked", async () => {
    const history = createMemoryHistory();
    render(
      <Router history={history}>
        <Home />
      </Router>
    );

    // check pre-nav page
    expect(screen.getByText(/Link to hello/i)).toBeInTheDocument();

    const leftClick = { button: 0 };
    userEvent.click(screen.getByText(/Link to hello/i), leftClick);

    // check we have navigated to the correct path - THIS PASSES!
    expect(history.location.pathname).toBe("/hello");

    // check that the content changed to the new page - THIS FAILS!
    expect(screen.getByText(/Hello component/i)).toBeInTheDocument();
  });

Ошибка ...

Ошибка, которую я, кажется, получаю, заключается в том, что он не может найти текст /Hello component/, все еще думая, что он находится в компоненте Home ... Это странно, потому что проверка местоположения в истории ясно показывает, что он находится на пути Hello.

Unable to find an element with the text: /Hello component/i. 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.

Ignored nodes: comments, <script />, <style />
<body>
  <div>
    <div
      data-testid="home-container"
    >
      <a
        href="/hello"
      >
        Link to hello
      </a>
    </div>
  </div>
</body>

person physicsboy    schedule 26.06.2021    source источник


Ответы (2)


    render(
      <Router history={history}>
        <App /> // <<--
      </Router>
    );

вы хотите использовать свой App компонент, поскольку у него есть маршруты, ваш Home компонент не знает о маршрутах.

person Noriller    schedule 26.06.2021
comment
Ааа ... Мне показалось странным, что официальная документация RTL показывала их только с помощью ‹App /›. Я полагаю, это имеет смысл ... Просто раздражает, потому что я хочу протестировать функциональность компонента, но не должен полагаться на импорт ‹App /› для тестирования компонента. - person physicsboy; 27.06.2021
comment
После дальнейшего рассмотрения и размышлений, основанных на моем предыдущем комментарии, я пришел к выводу, что я хочу провести модульный тест компонента, тогда как фактический тест, который я показал и что вы помогли решить, больше похожи на тест E2E / UAT. Я сам добавлю ответ и объясню, что происходит / чего я хочу и жду. - person physicsboy; 27.06.2021

Спасибо @BrunoNoriller за то, что помог мне увидеть ошибки моих путей здесь.

Основная проблема, с которой я столкнулся с моим первоначальным вопросом, заключалась в том, что я сбивал меня с толку с тем, что я думал, что меня тестируют.

Я думал, что выполняю модульное тестирование своего компонента, просто тестируя маршрутизацию, идущую от компонента, чтобы показать компонент <Hello/>. Фактически, то, что я действительно делал, было псевдо-E2E-тестом более широкого приложения и его маршрутизации, в котором - да - ответ Бруно был правильным, чтобы отобразить компонент <App /> и использовать историю окна для пусть тест знает, где мы находимся / куда идем.

На самом деле мне хотелось провести модульное тестирование компонента изолированно, и я просто забыл об этом. Основная аналогия заключается в том, чтобы представить, что ваш компонент выполняет какую-то handleClick функцию, которая запускается, когда вы нажимаете кнопку в этом компоненте. Когда дело доходит до тестирования, вам все равно, что это за запускаемая функция, просто мы передаем ее и проверяем, запускается ли она. Это похоже на маршрутизацию тестирования изоляции внутри компонента ... Допустим, у вас есть <Link>, который при нажатии будет маршрутизировать, как мой компонент, на /hello; На самом деле мы не заботимся о компоненте или странице, которые отображаются в конце этого (как в случае с тестом E2E), мы просто заботимся о том, чтобы что-то было отрисовано, и мы можем контролировать это в наш тест.

Home.jsx

return (
    <div data-testid="home-continer">
        <Link to="/hello">Link to hello</Link>
    </div>
);

Hello.jsx

return (
    <div data-testid="hello-continer">
        <Link to="/">Back</Link>
        <div>This is the real hello component</div>
    </div>
);

Home.spec.js

test('should render whatever is at /hello path when clicked', () => {
    /* 
      Essentially we are rendering a new, custom router for the 
      test to route to a test-specific version of the route we 
      render in the component under test
    */
    render(
      <BrowserRouter>
        <Home />
        <Route path="/hello">
          <div data-testid="hello-container">
             TEST HELLO
          </div>
        </Route>
      </BrowserRouter>
    );

    // Test we're at the correct starting point in the <Home> component
    expect(rendered.getByText(/Link to hello/i)).toBeInTheDocument();

    // Click the link to the /hello path
    fireEvent.click(rendered.getByText(/Link to hello/i));

    // Test we can now see the path-specific component contents
    expect(rendered.getByTestId("hello-container")).toBeTruthy();

    /* 
      This test shows that we are showing the test-specific path 
      contents, not the real <App/> path route component contents
    */
    expect(rendered.getByTestId("hello-container").textContent)
        .toContain("TEST HELLO");

});
person physicsboy    schedule 27.06.2021