React redux redux-saga 的集成测试

integration tests for react redux redux-saga

我有一个这样的项目结构:

app/
  global/
    styles/
    components/
  scenes/
    Home/
      actions.js
      constants.js
      index.jsx
      reducer.js
      sagas.js
      styles.styl
      index.spec.jsx
    some-other-scene/
      actions.js
      constants.js
      index.jsx
      reducer.js
      sagas.js
      styles.styl
      index.spec.jsx

所以我对这种结构的单元测试没有问题,但我对如何构建集成测试感到很困惑。对于我的单元测试,我将每个场景组件导出为 class

export class SomeComponent extends Component {}

并作为 redux 连接组件

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(SomeComponent)

所以对于第一种导出方式(class)我正在对其进行单元测试,但是对于第二种方式(连接组件方式)我不确定如何处理这个具体如何进行集成测试在 react/redux。我在互联网上搜索过这个,但没有找到与此结构接近的内容。

所以:

  1. 正在 react/redux/middleware(在本例中为 redux saga)中进行集成测试,一个组件如何与 redux 和中间件集成。
  2. 或者是关于整个应用程序如何与安装的所有组件一起工作?
  3. 如果它是#1,这是否意味着每个组件都应该有一个集成测试文件来测试该组件如何与 redux 和中间件集成,或者如果它是#2 那么它是否是一个测试文件将所有组件作为一个应用程序进行测试?

此外,如果它是#1,那么我应该如何通过 React 路由器测试路由?

对于您的示例,我们所做的是导出 mapStateToPropsmapDispatchToProps 并为它们编写单元测试(当它们不重要时)。

我想您已经对您的 action creators、sagas 和 reducers 进行了单元测试。

集成还剩下什么?您可能希望将组件挂载到真实商店的上下文中,以查看它们是否对 redux 商店的变化做出正确反应,以及它们是否派发了正确的操作。在我的项目中,我们使用端到端的浏览器自动化测试来验证这样的事情。

如果您想编写集成测试,请考虑为您的应用编写 UI 测试,以执行完整的端到端测试。有很多网络选项:

或 React Native:

至于单元测试,您应该能够逐个文件地进行,而不必导出整个组件。请参阅 redux saga 单元测试示例:https://github.com/redux-saga/redux-saga/blob/master/docs/advanced/Testing.md

好的,这是测试使用 Redux 的组件的一种方法。用故事书来演示。
首先,您需要在配置商店时接受初始状态:

import { createStore, applyMiddleware } from 'redux';
import rootReducer from './combineReducers';
import thunk from 'redux-thunk'; // In your case use saga

const ConfigureStore = (initialState) => {    
    let middleware = applyMiddleware(thunk);
    const store = initialState ?
        createStore(rootReducer, initialState, middleware)
        : createStore(rootReducer, middleware);
    return store;
};

export default ConfigureStore;

这样你就可以注入你的初始状态来测试一些特定的情况。
现在为了测试用户交互,您需要分派用户可以执行的操作,然后验证组件的状态。
看这个例子:

import React from 'react';
import configureStore from '../_setup';
import { storiesOf } from '@storybook/react';
import { Provider } from 'react-redux';
import { specs, describe, it } from 'storybook-addon-specifications'
import { configure,  mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { expect } from 'chai';
import Greeting from '../components/Greeting';


let store = configureStore();// Here initializing the store, you can pass the initial state also.
configure({ adapter: new Adapter() });

storiesOf('Greetings with Redux', module)
    .add('User says hello', () => {
        // here you pass the store to the component
        const storyWithProvider = (
            <Provider store={store}>
                <Greeting />
            </Provider>
        );
        // Here you execute the action you want to test.
        store.dispatch({
            type: 'SayHelloAction',
            payload: 'Jhon Doe'
        });

        specs(() => describe('User says hello', function () {
            it('Should have greeting message with the user name', function () {
                const output = mount(storyWithProvider);
                // Here you verify the state of the component
                expect(output.text()).to.contains('Hello: Jhon Doe');
            });
        }));
        return storyWithProvider;
    });

您还可以执行多个操作,以获得您想要的结果。
例如

        store.dispatch({
            type: constants.actions.SHOW_VENDOR_PRODUCTS,
            payload: selectedVendor
        });
        store.dispatch({
            type: constants.actions.VENDOR_ACCEPTS_ORDER,
            payload: false
        });
        store.dispatch({
            type: constants.actions.ADD_PRODUCT,
            payload: selectedProduct
        });

然后验证结果:

expect(wrapper.find('.btn .btn-lg .btn-success')).to.have.length.of(1);

有关参考,请参阅此 example project,请参阅规格选项卡以验证测试:

这里我用storybook来演示,你也可以用简单的mocha来演示。
希望对您有所帮助。