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

我正在为一个组件编写集成测试,该组件应根据异步(重击)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);

我想编写一个测试来断言该组件已重定向到预期路径。

我正在使用this technique来检查实际的重定向路径(虽然不完美,但这不是此问题的重点)。

卡住的地方是在执行{uxux / thunk}操作后.then()中的状态更改。因为这是一个承诺,所以重定向会在我的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的完整说明。)因此,现在,我的测试(正确地)没有击中预期的路线,而是正确地识别了在执行重击操作时出现的加载屏幕。

认为正确的方法是模拟asyncThunkaction的响应,以便它返回具有匹配数据的已解决的promise,但是到目前为止,我还无法弄清楚怎么做。我遵循manual mocks上的Jest文档,并创建了相应的模拟文件:

// __mocks__/actions.js
const asyncThunkaction = function(){
    return Promise.resolve({foo: 'bar'});
};
export { asyncThunkaction };

...但是我的测试仍然“看到”加载状态。我什至不认为它正在查看我的模拟文件/操作。

正确的方法是什么?

zhuhui34555 回答:如何测试分派Redux / Thunk操作的React组件

诚然,这是对您问题的横向回答,但是我建议您尝试一下测试库及其体现的理想,尤其是对于集成测试。

它有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

由于此方法中的模拟较少,因此如何实现此目标的细节可能超出了您所给出的示例的范围,但是上面的示例链接对入门起了很大的作用。

,

这是我如何使这项工作有效的“秘诀” ...

使用testing-library/react ...

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

(此建议+1到@tmahle)

通过创建一个“ manual mock”来模拟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问题中都“存在”……但是我花了很长时间才将它们拼凑在一起。希望这可以节省您的时间。

本文链接:https://www.f2er.com/3121235.html

大家都在问