Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning: An update to App inside a test was not wrapped in act(...) in enzyme and hooks

I have written this component. it fetchs data using hooks and state. Once it is fetched the loading state is changed to false and show the sidebar.

I faced a problem with Jest and Enzyme, as it does throw a warning for Act in my unit test. once I add the act to my jest and enzyme the test is failed!

// @flow
import React, { useEffect, useState } from 'react';
import Sidebar from '../components/Sidebar';
import fetchData from '../apiWrappers/fetchData';

const App = () => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const getData = async () => {
      try {
        const newData = await fetchData();
        setData(newData);
        setLoading(false);
      }
      catch (e) {
        setLoading(false);
      }
    };
    getData();
    // eslint-disable-next-line
  }, []);
  return (
    <>
      {!loading
        ? <Sidebar />
        : <span>Loading List</span>}
    </>
  );
};
export default App;

And, I have added a test like this which works perfectly.

import React from 'react';
import { mount } from 'enzyme';
import fetchData from '../apiWrappers/fetchData';
import data from '../data/data.json';
import App from './App';

jest.mock('../apiWrappers/fetchData');

const getData = Promise.resolve(data);
fetchData.mockReturnValue(getData);

describe('<App/> Rendering using enzyme', () => {
  beforeEach(() => {
    fetchData.mockClear();
  });

  test('After loading', async () => {
    const wrapper = mount(<App />);
    expect(wrapper.find('span').at(0).text()).toEqual('Loading List');

    const d = await fetchData();
    expect(d).toHaveLength(data.length);

    wrapper.update();
    expect(wrapper.find('span').exists()).toEqual(false);
    expect(wrapper.html()).toMatchSnapshot();
  });
});

So, I got a warning:

Warning: An update to App inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});

I did resolve the warning like this using { act } react-dom/test-utils.

import React from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import fetchData from '../apiWrappers/fetchData';
import data from '../data/data.json';
import App from './App';

jest.mock('../apiWrappers/fetchData');

const getData = Promise.resolve(data);
fetchData.mockReturnValue(getData);

describe('<App/> Rendering using enzyme', () => {
  beforeEach(() => {
    fetchData.mockClear();
  });

  test('After loading', async () => {
    await act(async () => {
      const wrapper = mount(<App />);
      expect(wrapper.find('span').at(0).text()).toEqual('Loading List');

      const d = await fetchData();
      expect(d).toHaveLength(data.length);

      wrapper.update();
      expect(wrapper.find('span').exists()).toEqual(false);
      expect(wrapper.html()).toMatchSnapshot();
    });
  });
});

But, then my test is failed.

<App/> Rendering using enzyme › After loading

expect(received).toEqual(expected) // deep equality

Expected: false
Received: true

  35 | 
  36 |       wrapper.update();
> 37 |       expect(wrapper.find('span').exists()).toEqual(false);

Does anybody know why it fails? Thanks!

"react": "16.13.1",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.3",
like image 579
Danial Avatar asked Aug 25 '20 01:08

Danial


People also ask

When should you wrap for the act?

So you're supposed to wrap every interaction you make with your component in act to let React know that we expect our component to perform some updates and when you don't do that and there are updates, React will warn us that unexpected updates happened.

Does enzyme work with hooks?

A React Function Component is simply a function that returns a React element. With React 16.8 the most awaited feature, hooks was introduced which allowed for injecting state and lifecycle methods into stateless function components and make it stateful.

What is enzyme in testing?

An enzyme marker is a blood test to measure the levels of specific enzymes in your blood. Musculoskeletal diseases, organ damage and injuries can cause enzymes to leak from cells into your blood. Your healthcare provider may refer to enzyme markers as biomarkers (biological markers).

What type of testing does enzyme allow us to perform?

Enzyme is a JavaScript Testing utility for React that makes it easier to test your React Components' output. You can also manipulate, traverse, and in some ways simulate runtime given the output. Enzyme's API is meant to be intuitive and flexible by mimicking jQuery's API for DOM manipulation and traversal.


1 Answers

This issue is not new at all. You can read the full discussion here: https://github.com/enzymejs/enzyme/issues/2073.

To sum up, currently in order to fix act warning, you have to wait a bit before update your wrapper as following:

const waitForComponentToPaint = async (wrapper) => {
  await act(async () => {
    await new Promise(resolve => setTimeout(resolve));
    wrapper.update();
  });
};

test('After loading', async () => {
  const wrapper = mount(<App />);
  expect(wrapper.find('span').at(0).text()).toEqual('Loading List');
  
  // before the state updated
  await waitForComponentToPaint(wrapper);
  // after the state updated

  expect(wrapper.find('span').exists()).toEqual(false);
  expect(wrapper.html()).toMatchSnapshot();
});
like image 183
tmhao2005 Avatar answered Oct 13 '22 23:10

tmhao2005