简体   繁体   中英

When testing a React component that renders fetched API data, how best to wait for that data before making any assertions?

I have a simple "Edit User" component which contains a form with a "First Name" field. When the component first renders, that field is blank until an API request for the user's data is completed. Once the data is fetched, the state gets updated and the component re-renders, this time with the "First Name" field populated. (To be clear, no user interaction is required for this to happen.)

My test, which expects the "First Name" field to be correctly populated, is currently failing because it's only testing the initial render of my component, before the user info can be fetched.

What's an efficient way to test this behavior ie wait until the (mocked) request is completed before any assertions are made?

FWIW, I'm using React 16.12.0.

Here's the abbreviated component:

import React, { useState, useEffect } from 'react'
import axios from 'axios'

const EditUser = (props) => {
  const [user, setUser] = useState({
    fname: "",
    lname: ""
  })

  const fetchUser = async () => {
    await axios.get(`/api/users/${props.userId}`)
    .then(response => {
      const data = response.data
      const user = {
        fname: data.attributes.fname,
        lname: data.attributes.lname
      }
      setUser(user)
    })
    .catch(error => console.log(error))
  }

  useEffect(() => {
    fetchUser()
  }, [])

  return (
    <div>
      <h2>Edit User</h2>
      <form>
        <div className="form-group">
          <label>First Name</label>
          <input
            name="fname"
            value={user.fname}
          />
        </div>
      </form>
    </div>
  )
}

export default EditUser

And the test:

import React from 'react'
import { mount } from 'enzyme'
import { act } from 'react-dom/test-utils'
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
import EditUser from 'packs/editUser'

describe('editUser', () => {
  let wrapper
  const axiosMock = new MockAdapter(axios)

  axiosMock.onGet('/api/users/5').reply(200, {
    id: 5,
    attributes: {
      fname: 'Bradley',
      lname: 'King'
    }
  })

  it('shows the user edit form', async () => {
    await act(async () => {
      wrapper = await mount(<EditUser userId={5} />)
    })

    const firstNameInput = wrapper.find('input[name="fname"]')

    // the following FAILS because the first name input is blank
    // _until_ the user data is fetched and the component is
    // re-rendered with field populated
    expect(firstNameInput.props().value).toEqual('Bradley')
  })
})

Thanks!

Since you are expecting a render to have occurred, you can use the .update() method of the wrapper to

Sync the enzyme component tree snapshot with the react component tree. Useful to run before checking the render output if something external may be updating the state of the component somewhere.

it('shows the user edit form', async() => {
  await act(async() => {
    wrapper = await mount(<EditUser userId={5} />)
  })

  wrapper.update();

  const firstNameInput = wrapper.find('input[name="fname"]')

  expect(firstNameInput.props().value).toEqual('Bradley')
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM