为了账号安全,请及时绑定邮箱和手机立即绑定

如何测试分派 Redux / Thunk 操作的 React 组件

如何测试分派 Redux / Thunk 操作的 React 组件

凤凰求蛊 2021-12-23 19:44:01
我正在为一个组件编写集成测试,该组件应该根据异步(thunk)redux 操作的响应重定向到特定路径。这是我的组件的简化版本:class MyComponent extends React.Component {  constructor(props) {    super(props);    this.state = {      redirect: false    }    this.props.dispatch(asyncThunkAction())      .then( () => this.setState({redirec: true}) )      .catch( (err) => console.log('action failed') )  } ...  render() {    if (this.state.redirect) {      return <Redirect to='/whocares' />    }    ...  }}function mapStateToProps(state) {  return {     ...  };}export default connect(mapStateToProps)(MyComponent);我想编写一个测试,断言组件重定向到预期路径。我正在使用这种技术来检查实际的重定向路径(它并不完美,但不是这个问题的重点)。我卡住的地方是.then()下面 redux/thunk 动作中的状态变化。因为这是一个承诺,重定向发生在我的expect声明之后,所以我无法测试。这是我的测试的样子:const middlewares = [thunk];const mockStore = configureStore(middlewares);  test('redirects after thunk action', () => {    const redirectUrl = '/whocares'    const data = {};    jest.mock('../actions');    act(() => {      ReactDOM.render(        <TestRouter            ComponentWithRedirection={<MyComponent store={mockStore(data)} />}            RedirectUrl={redirectUrl}        />,         container);    });    expect(container.innerHTML).toEqual(      expect.stringContaining(redirectUrl)    )  })我的 TestRouter 只是将预期的重定向 URL 打印到 DOM 中。(查看上面的链接以获取有关此 hack 的完整说明。)因此,现在我的测试(正确)识别了在 thunk 操作进行时出现的加载屏幕,而不是到达预期的路线。我认为这样做的正确方法是模拟响应,asyncThunkAction以便它返回具有匹配数据的已解决承诺,但到目前为止我还没有弄清楚如何做到这一点。我按照手动模拟的 Jest 文档创建了相应的模拟文件:// __mocks__/actions.jsconst asyncThunkAction = function(){    return Promise.resolve({foo: 'bar'});};export { asyncThunkAction };...但我的测试仍然“看到”加载状态。我什至不认为它正在查看我的模拟文件/操作。这样做的正确方法是什么?
查看完整描述

2 回答

?
牛魔王的故事

TA贡献1830条经验 获得超3个赞

这是我如何让这个工作的“食谱”......


使用测试库/反应...


import { render, fireEvent, waitForElement, act } from '@testing-library/react';

(+1 给@tmahle 这个建议)


通过创建“手动模拟”来模拟 axios(或者在我的情况下是包装它的 API 模块),这基本上需要__mocks__在包含同名文件的真实文件旁边创建一个目录。然后导出一个具有替换get方法(或您的代码使用的任何一个)的属性的对象。


//__mocks__/myclient.js

export default {

  get: jest.fn(() => Promise.resolve({ data: {} }))

};

即使您不在测试中调用模拟代码,您也需要import在测试文件中调用它...


import myapi from '../api/myapi';

jest.mock('../api/myai');

您可以模拟来自模拟 API 调用的响应,如下所示:


myapi.get.mockResolvedValueOnce({

  data: { foo: "bar" },

});

我对这部分有点模糊......即使模拟的 API 请求立即以已解决的承诺响应,您可能需要wait编写expects


const { getByText, getByTestId, container } = render(<MyComponent />);

await wait(() => getByText('Some text that appears after the '));

expect(container.innerHTML).toEqual('whatever');

所有这些都在各种文档和 SO 问题中“出现在那里”......但我花了很长时间才把它们拼凑在一起。希望这可以节省您的时间。


查看完整回答
反对 回复 2021-12-23
?
回首忆惘然

TA贡献1847条经验 获得超11个赞

诚然,这是对您的问题的一个侧面回答,但我建议您尝试使用 testing-library 及其所体现的理想,尤其是对于集成测试。

它在 DOM 和 React 两种风格中都可用,使用哪一种可能取决于您的重定向发生在什么抽象级别:

https://github.com/testing-library/dom-testing-library https://github.com/testing-library/react-testing-library

使用此范例,您不会尝试断言用户被重定向到正确的路径,而是在重定向后屏幕上显示正确的内容。您还可以将您的模拟限制在绝对必要的范围内(如果您正在进行真正的集成测试,则您的测试环境无法模拟的可能没有或只有浏览器 API)。

这里的整体方法可能会让你模拟更少,并且可能渲染应用程序的更大部分。一个可能有用的例子可以在这里找到:https : //codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/master/?fontsize=14&module=%2Fsrc%2F__tests__%2Freact -router.js&previewwindow=tests

由于这种方法中的嘲讽较少,因此您如何实现这一点的具体细节可能超出了您所提供示例的范围,但上面的示例链接应该对入门有很大帮助。


查看完整回答
反对 回复 2021-12-23
  • 2 回答
  • 0 关注
  • 149 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信