reactjsreact-testing-librarymoxios

Wrapping async moxios call in act callback


I am trying to test a react functional component using hooks. The useEffect hook makes a call to a third part API which then calls setState on return.

I have the test working but keep getting a warning that an update to the component was not wrapped in act.

The problem I have is that the expectation is inside a moxios.wait promise and therefore I cannot wrap that in an act function and then assert on the result of that.

The test passes but I know not wrapping code that updates state in an act function could lead to false positives or uncovered bugs. I'm just wondering how I should be testing this.

I've tried using the new async await act function in the react 16.9.0 alpha release as well as numerous suggestions I've found in many github issues like jest setTimers and none seem to solve the issue.

The component

 const Benefits = props => {
  const [benefits, setBenefits] = useState([])
  const [editing, setEditing] = useState(false)
  const [editingBenefit, setEditingBenefit] = useState({id: null, name: '', category: ''})

  useEffect(() => {
    axios.get('#someurl')
      .then(response => {
        setBenefits(response.data)
    })
  }, [])
}

The test

describe('Benefits', () => {
  it('fetches the list of benefits from an api and populates the benefits table', (done) => {
    const { rerender } = render(<Benefits />)
    moxios.wait(() => {
      const request = moxios.requests.mostRecent()
      request.respondWith({
        status: 200,
        response: benefits
      }).then(() => {
        expect(document.querySelectorAll('tbody > tr').length).toBe(2)
        done()
      })
    })
  })
})

The test passes but I get the following warning

Warning: An update to Benefits 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 */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser.
    in Benefits (at benefits.spec.js:28)

Solution

  • from react 16.9.0 you can use async/await act

    Your code should look like this

    describe('Benefits', () => {
      it('fetches the list of benefits from an api and populates the benefits table', async() => {
        const { rerender } = render(<Benefits />);
    
        await moxios.wait(jest.fn);
        await act(async() => {
          const request = moxios.requests.mostRecent()
          await request.respondWith({
            status: 200,
            response: benefits
          });
        });
    
        expect(document.querySelectorAll('tbody > tr').length).toBe(2)
    })
    

    I use jest.fn in moxios.wait because it needs callback function