Redux:如何测试连接的组件?

Redux: How to test a connected component?

我正在使用 Enzyme 对我的 React 组件进行单元测试。我知道为了测试原始未连接的组件,我必须导出它并测试它(我已经做到了)。我已经设法为连接的组件编写了一个测试,但我真的不确定这是否是正确的方法,也不确定我到底想为连接的组件测试什么。

Container.jsx

import {connect} from 'react-redux';
import Login from './Login.jsx';
import * as loginActions from './login.actions';

const mapStateToProps = state => ({
  auth: state.auth
});
const mapDispatchToProps = dispatch => ({
  loginUser: credentials => dispatch(loginActions.loginUser(credentials))

});
export default connect(mapStateToProps, mapDispatchToProps)(Login);

Container.test.js

import React from 'react';
import {Provider} from 'react-redux';
import {mount, shallow} from 'enzyme';
import {expect} from 'chai';
import LoginContainer from '../../src/login/login.container';
import Login from '../../src/login/Login';


describe('Container Login', () => {
  it('should render the container component', () => {
    const storeFake = state => ({
      default: () => {
      },
      subscribe: () => {
      },
      dispatch: () => {
      },
      getState: () => ({ ...state })
    });
    const store = storeFake({
      auth: {
        sport: 'BASKETBALL'
      }
    });

    const wrapper = mount(
      <Provider store={store}>
        <LoginContainer />
      </Provider>
    );

    expect(wrapper.find(LoginContainer).length).to.equal(1);
    const container = wrapper.find(LoginContainer);
    expect(container.find(Login).length).to.equal(1);
    expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' });
  });
});

正如您所指出的,我通常这样做的方法是将未连接的组件也导出,然后进行测试。

export {Login};

这是一个例子。 Source of the component, and source of the tests.

对于包装组件,我不会为它们编写测试,因为我的映射(mapStateToPropsmapDispatchToProps)通常非常简单。如果我想测试一个包装组件,我真的只是在测试那些地图。所以我会选择明确测试这些,而不是以包装形式重新测试整个组件。

有两种方法可以测试这些功能。一种方法是在模块本身中导出函数。

即;

export {mapStateToProps, mapDispatchToProps}

我不太喜欢这个,因为我不希望应用程序中的其他模块访问它们。在我的测试中,我有时使用 babel-plugin-rewire 来访问 "in-scope" 变量,所以这就是我在这种情况下会做的。

这可能看起来像:

import {
  Login, __Rewire__
}

const mapStateToProps = __Rewire__.__get__('mapStateToProps');

describe('mapStateToProps', () => { ... });

这是一个有趣的问题。

我通常会同时导入容器和组件来进行测试。对于我使用的容器测试,redux-mock-store。组件测试用于测试异步函数。例如,在您的情况下,登录过程是使用 sinon 存根的异步函数。这是相同的片段,

import React from 'react';
import {Provider} from 'react-redux';
import {mount, shallow} from 'enzyme';
import {expect} from 'chai';
import LoginContainer from '../../src/login/login.container';
import Login from '../../src/login/Login';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { stub } from 'sinon';

const mockStore = configureMockStore([thunk]);

describe('Container Login', () => {
  let store;
  beforeEach(() => {
    store = mockStore({
      auth: {
        sport: 'BASKETBALL',
      },
    });
  });
  it('should render the container component', () => {
    const wrapper = mount(
      <Provider store={store}>
        <LoginContainer />
      </Provider>
    );

    expect(wrapper.find(LoginContainer).length).to.equal(1);
    const container = wrapper.find(LoginContainer);
    expect(container.find(Login).length).to.equal(1);
    expect(container.find(Login).props().auth).to.eql({ sport: 'BASKETBALL' });
  });

  it('should perform login', () => {
    const loginStub = stub().withArgs({
      username: 'abcd',
      password: '1234',
    });
    const wrapper = mount(<Login
      loginUser={loginStub}
    />);
  wrapper.find('button').simulate('click');
  expect(loginStub.callCount).to.equal(1);
  });
});

如果我们遇到路由器问题,可以考虑将路由器库添加到测试文件中,例如:

import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import { mount } from 'enzyme';
import ReadDots from './ReadDots';

const storeFake = state => ({
  default: () => {
  },
  subscribe: () => {
  },
  dispatch: () => {
  },
  getState: () => ({ ...state })
});

const store = storeFake({
  dot: {
    dots: [
      {
        id: '1',
        dot: 'test data',
        cost: '100',
        tag: 'pocket money'
      }
    ]
  }
});

describe('<ReadDots />', () => {
  it('should render ReadDots component', () => {
    const component = mount(
      <Provider store={store}>
        <Router>
          <ReadDots />
        </Router>
      </Provider>
    );
    expect(component.length).toEqual(1);
  });
});