簡體   English   中英

如何在異步調用反應后測試 state 更新和組件重新渲染

[英]How to test state update and component rerender after async call in react

我正在編寫一個簡單的應用程序,在單擊按鈕后,應該執行異步調用來識別 API,當 promise 解決時,它應該更新組件的 state。 我正在使用反應掛鈎來管理組件中的 state。

在我的測試中,我模擬了 API 調用。

發現.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
  }
}

現場模擬:

const getUserInfoMock = jest.fn();

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

export default mock;

用戶.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>
  );
};

我的問題是如何正確測試這種行為。 我設法讓它通過了,但不是在simulate()上調用await一個丑陋的黑客嗎? 模擬不返回 promise。 這是一個測試:

  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);
  });

另一方面,當我只檢查是否調用了 mock 時,我不需要使用 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 掛鈎。 Enzyme 是否完全支持反應鈎子? 我也遇到警告警告:即使我包裝了mountsimulate功能Warning: An update to User inside a test was not wrapped in act(...)

您應該按照Dan Abramov 的博客文章將影響渲染的調用包裝在 async act() function 中,如下所示:

  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);
  });

參考: 當模擬點擊調用 function 調用 promise 時使用 React 的 Jest 和 Enzyme 進行測試

將您的期望語句包裝在setImmediate

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM