javascriptreactjsreact-hooksjestjsreact-test-renderer

How correctly write Jest test for avoid act() warning?


useEffect usually use Promises for update state.

This updates cause long warning in jest: Warning: An update to null inside a test was not wrapped in act(...).

How correctly write Jest Test for such case?

live example, Reproducible Example:

https://codesandbox.io/s/jest-test-for-useeffect-with-promises-spieq?file=/index.test.js

index.test.js

import React from "react";
import Hello from "./Hello";
import { create, act } from "react-test-renderer";

it("works", () => {
  let root;
  act(() => {
    root = create(<Hello />);
  });

  // console.log("From test:", );
  let repr = JSON.stringify(root.toJSON());
  expect(repr).toBe('{"type":"span","props":{},"children":["Hello! "]}');
});

Hello.js

import React, { useState, useEffect } from "react";

export default () => {
  const [count, setCount] = useState();

  useEffect(() => {
    Promise.resolve({}).then(() => setCount(4));
  }, []);

  return <span>Hello! {count}</span>;
};

enter image description here

Upd 1:

the same result for one of propasal:

enter image description here


Solution

  • I would like to suggest you to use testing-library/react which could be use smoothly with jest and provide async methods

    Your test would become something like:

    import React from "react";
    import Hello from "./Hello";
    import { render } from '@testing-library/react';
    
    it("works", () => {
       const { baseElement } = render(<Hello />);
       expect(baseElement).toBeTruthy();
    });
    

    EDIT: For sake of completeness, in your example you're trying to test whether or not an update in your component happened after an effect which is using a promise.

    In order to waitFor something async you can use the method findBy (findBy methods are a combination of getBy queries and waitFor.)

    import React from "react";
    import Hello from "./Hello";
    import { render, findByText } from "@testing-library/react";
    import "@testing-library/jest-dom";
    
    it("works", async () => {
      const { baseElement } = render(<Hello />);
      expect(await findByText(baseElement, "Hello! 4")).toBeVisible();
    });
    

    Let's me explain, import "@testing-library/jest-dom" allows you to use the the method toBeVisible

    So, this test is rendering a component Hello and then it's waiting (with default value, 1000ms) inside the baseElement (which is the component itself) looking for the text "Hello! 4", then if found it will return the found element, which we are going to test whether is it visibile or not.