在异步调用中如何测试状态更新和组件重新呈现

时间:2019-11-13 19:28:35

标签: javascript reactjs unit-testing asynchronous enzyme

我正在编写一个简单的应用程序,在该应用程序中,单击按钮后,应执行对Spotify API的异步调用,并在promise解析后应更新组件的状态。我正在使用反应挂钩来管理组件中的状态。

在测试中,我模拟了API调用。

spotify.jsx

export default class Spotify {
  constructor(token) {
    this.axiosInstance = axios.create({
      baseURL: baseURL,
      headers: buildHeaders(token),
    });
  }

  async getUserInfo() {
    const userInfo = await this.axiosInstance({
      url: `/me`,
    });
    return userInfo.data
  }
}

spotify模拟:

const getUserInfoMock = jest.fn();

const mock = jest.fn().mockImplementation(() => ({
  getUserInfo: getUserInfoMock,
}));

export default mock;

User.jsx

const User = props => {
  const [user, setUser] = useState(null);
  const {token} = useContext(AuthContext);
  const spotify = useMemo(() => new Spotify(token), [token]);

  const getUserInfo = async () => {
    console.log("button clicked")
    const fetched = await spotify.getUserInfo();
    console.log(fetched)
    setUser(fetched);
  }

  return (
    <React.Fragment>
      <p>user page</p>
      <button onClick={getUserInfo} > click me </button>
      {user && (
        <div>
          <p>{user.display_name}</p>
          <p>{user.email}</p>
        </div>
      )}
    </React.Fragment>
  );
};

我的问题是如何正确测试这种行为。我设法使其通过,但不是在await上调用simulate()是一个丑陋的骇客吗?模拟不返回承诺。这是一个测试:

  it('updates display info with data from api', async () => {
    const userInfo = {
      display_name: 'Bob',
      email: 'bob@bob.bob',
    };
    spotifyMock.getUserInfo.mockImplementation(() => Promise.resolve(userInfo));

    wrapper = mount(<User />);
    expect(wrapper.find('p')).toHaveLength(1);
    await wrapper
      .find('button')
      .last()
      .simulate('click');

    wrapper.update();
    expect(wrapper.find('p')).toHaveLength(3);
  });

另一方面,当我仅检查是否调用了模拟程序时,我不需要使用async / await和测试通过:

  it('calls spotify api on click', () => {
    wrapper = mount(<User />);
    expect(spotifyMock.getUserInfo).not.toHaveBeenCalled();
    wrapper
      .find('button')
      .last()
      .simulate('click');
    expect(spotifyMock.getUserInfo).toHaveBeenCalledTimes(1);
  });

我想知道我的测试方法是否正确,以及是否想添加一个功能以在组件渲染时从api中获取数据-使用useEffect挂钩。酶是否完全支持react挂钩? 即使包装了Warning: An update to User inside a test was not wrapped in act(...)mount函数,我也难以警告simulate

2 个答案:

答案 0 :(得分:1)

参考:Testing with React's Jest and Enzyme when simulated clicks call a function that calls a promise

setImmediate中包裹您的期望声明

setImmediate(() => {
    expect(spotifyMock.getUserInfo).toHaveBeenCalledTimes(1);
})

答案 1 :(得分:0)

您应该按照Dan Abramov's blog post将影响渲染的调用包装在一个异步act()函数中,如下所示:

  it('calls spotify api on click', async () => {
    await act(async () => {
      wrapper = mount(<User />);
    });
    expect(spotifyMock.getUserInfo).not.toHaveBeenCalled();

    await act(async () => {
      wrapper
        .find('button')
        .last()
        .simulate('click');
    });

    wrapper.update();
    expect(spotifyMock.getUserInfo).toHaveBeenCalledTimes(1);
  });