Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test react-toastify with jest and react-testing-library

I have a screen with some form, and on submission, I send the request to back-end with axios. After successfully receiving the response, I show a toast with react-toastify. Pretty straight forward screen. However, when I try to test this behavior with an integration test using jest and react testing library, I can't seem to make the toast appear on DOM.

I have a utility renderer like that to render the component that I'm testing with toast container:

import {render} from "@testing-library/react";
import React from "react";
import {ToastContainer} from "react-toastify";

export const renderWithToastify = (component) => (
  render(
    <div>
      {component}
      <ToastContainer/>
    </div>
  )
);

In the test itself, I fill the form with react-testing-library, pressing the submit button, and waiting for the toast to show up. I'm using mock service worker to mock the response. I confirmed that the response is returned OK, but for some reason, the toast refuses to show up. My current test is as follows:

expect(await screen.findByRole("alert")).toBeInTheDocument();

I'm looking for an element with role alert. But this seems to be not working. Also, I tried doing something like this:

...

beforeAll(() => {
  jest.useFakeTimers();
}

...

it("test", () => {
  ...

  act(() =>
    jest.runAllTimers();
  )
  expect(await screen.findByRole("alert")).toBeInTheDocument();
}

I'm kind of new to JS, and the problem is probably due to asynch nature of both axios and react-toastify, but I don't know how to test this behavior. I tried a lot of things, including mocking timers and running them, mocking timers and advancing them, not mocking them and waiting etc. I even tried to mock the call to toast, but I couldn't get it working properly. Plus this seems like an implementation detail, so I don't think I should be mocking that.

I think the problem is I show the toast after the axios promise is resolved, so timers gets confused somehow.

I tried to search many places, but failed to find an answer.

Thanks in advance.

like image 487
Fatih Doğmuş Avatar asked Nov 17 '20 12:11

Fatih Doğmuş


People also ask

How do you use Toastify in react?

Install Toastify Package Run the code on the terminal, and the app should open up in the browser. Once you've created the react application, install the Toastify package by running the command below. See the react toastify GitHub repository for more info about this package.

Should I use Jest or react testing library?

The React community recommends Jest as the React testing framework of choice.

What is the use of jest in react testing?

Jest is a test runner that finds tests, runs the tests, and determines whether the tests passed or failed. Additionally, Jest offers functions for test suites, test cases, and assertions. React Testing Library provides virtual DOMs for testing React components.

What is @testing-library/React?

Testing library has three packages which we use commonly: @testing-library/react for rendering elements to the DOM and querying those elements, @testing-library/jest-dom for additional Jest assertions (toBeInTheDocument) and @testing-library/user-event for firing events on the rendered elements.

Do I need to test react?

The team at React tests React; therefore, there is no need for you to test React’s functionality such as state, componentDidMount, etc. The same goes for other libraries you may use. What to test? When component testing in React, the focus should be on replicating how the user would interact with the React component.

What is component testing in react?

When component testing in React, the focus should be on replicating how the user would interact with the React component.


1 Answers

Thank you @Estus Flask, but the problem was much much more stupid :) I had to render ToastContainer before my component, like this:

import {render} from "@testing-library/react";
import React from "react";
import {ToastContainer} from "react-toastify";

export const renderWithToastify = (component) => {
  return (
    render(
      <div>
        <ToastContainer/>
        {component}
      </div>
    )
  );
};

Then, the test was very simple, I just had to await on the title of the toast:

expect(await screen.findByText("alert text")).toBeInTheDocument();

The findByRole doesn't seem to work for some reason, but I'm too tired to dig deeper :) I didn't have to use any fake timers or flush the promises. Apperently, RTL already does those when you use await and finBy* queries, only the order of rendering was wrong.

like image 161
Fatih Doğmuş Avatar answered Sep 23 '22 19:09

Fatih Doğmuş