react-redux 4.0:测试mapStateToProps和mapDispatchToProps?

时间:2018-12-21 15:56:00

标签: reactjs redux react-redux

react-redux 4.0现在使用上下文API,这太棒了!但是,当尝试更新我的项目时,一切正常,但是我的单元测试开始失败。

就上下文而言,这是我非常有趣的“样板”项目。出于演示目的,它基本上是一个待办事项应用程序(我们都喜欢待办事项,对吧?)。

现在,对于我的容器,我进行了一些单元测试,但是非常基础。基本上,我想确保mapStateToProps和mapDispatchToProps都收到他们应该收到的东西。我不在乎如何,只是他们进来了。

这是我的“ ConnectedToDos”组件:

import { injectIntl } from 'react-intl'
import { connect } from 'react-redux'
import { createStructuredSelector } from 'reselect'

import {
  addTodo,
  completeTodo,
  deleteTodo,
  loadTodos,
  makeGetTodos,
} from './duck'

import ToDos from './view'

const mapStateToProps = createStructuredSelector({
  todos: makeGetTodos(),
})

const mapDispatchToProps = {
  handleComplete: completeTodo,
  handleSubmit: addTodo,
  handleDelete: deleteTodo,
  requestTodos: loadTodos,
}

export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ToDos))

现在,要对此进行测试,我曾经做过以下事情:

import React from 'react'
import PropTypes from 'prop-types'
import configureStore from 'redux-mock-store'
import { shallow, mount } from 'enzyme'
import { fromJS } from 'immutable'
import { MemoryRouter } from 'react-router-dom'
import { IntlProvider } from 'react-intl'

import ConnectedToDos from 'modules/ToDos'

describe('Connected <ToDos />', () => {
  const initialState = fromJS({
    resources: {
      todos: [
        { title: 'A todo', description: 'Do me!', id: '1', done: false },
        { title: 'A todo', description: 'Do me!', id: '2', done: true },
        { title: 'A todo', description: 'Do me!', id: '3', done: false },
        { title: 'A todo', description: 'Do me!', id: '4', done: true },
      ],
    },
  })

  let mockStore
  let store

  beforeAll(() => {
    mockStore = configureStore([])
    store = mockStore(initialState)
  })

  it('props should match mapStateToProps', () => {
    const wrapper = shallow(<ConnectedToDos store={store} />)

    expect(wrapper.prop('todos')).toEqual(initialState.getIn(['resources', 'todos']))
  })

  it('props should match mapDispatchToProps', () => {
    const wrapper = mount(
      <IntlProvider locale="en" messages={{ en: {} }}>
        <MemoryRouter>
          <ConnectedToDos />
        </MemoryRouter>
      </IntlProvider>,
      {
        context: { store },
        childContextTypes: { store: PropTypes.object.isRequired },
      },
    )

    wrapper.find('input[type="checkbox"]').first().simulate('change')
    wrapper.find('form').simulate('submit')
    wrapper.find('Icon[name="DELETE"]').first().simulate('click')

    const actions = store.getActions()
    const todosActions = actions.filter(action => action.type.includes('/todos/'))

    expect(todosActions).toHaveLength(4)
  })
})

但是,这现在引发了一个很好的警告:

  

不变违反:在以下情况下找不到“存储”   “连接(InjectIntl​​(ToDos))”。可以将根组件包装在   <Provider>,或将自定义React上下文提供程序传递给<Provider>,然后   Connect(InjectIntl(ToDos))的相应React上下文使用者   在连接选项中。

很酷。但是现在我想知道测试容器的最佳方法是什么?

我尝试了无数种方法,最新的方法如下:

  it('props should match mapStateToProps', () => {
    const wrapper = shallow(<ConnectedToDos />, { context: { store } })

    expect(wrapper.prop('todos')).toEqual(initialState.getIn(['resources', 'todos']))
  })

但是失败了。看来,无论我做什么,wrapper.context()始终是一个空对象。

我无法在此处复制粘贴所有代码,但是我有一个可以克隆并自己玩的存储库:https://github.com/enrique-ramirez/react-redux-boilerplate

失败的测试在src/modules/ToDos/tests/index.test.js上。

TL; DR 如何使用新的react-redux测试mapStateToPropsmapDispatchToProps

1 个答案:

答案 0 :(得分:0)

上面的

Shubham khatri's solution对我不起作用,所以我不得不在周末花一些时间。这就是我最终得到的:

对于mapStateToProps,我基本上将其安装在所需的提供程序中(对我来说,它是商店提供程序,还有IntlProvider和MemoryRouter;但是唯一相关的是redux的<Provider>)。然后,找到视图(不是连接的组件,而是普通的组件)并将其所有道具提取到变量中。然后,我可以检查每个道具是否与我商店中的物品相匹配,这正是我要测试的内容(道具已从商店中正确映射):

  it('props should match mapStateToProps', () => {
    const wrapper = mount(
      <Provider store={store}>
        <IntlProvider locale="en" messages={{ en: {} }}>
          <MemoryRouter>
            <ConnectedToDos />
          </MemoryRouter>
        </IntlProvider>
      </Provider>,
      {},
    )
    const props = wrapper.find(ToDos).props()

    expect(props.todos).toEqual(initialState.getIn(['resources', 'todos']))
  })

对于mapDispatchToProps,它几​​乎与以前相同,但是除了在上下文中传递商店之外,我不得不将所有内容都包装在redux的<Provider>中,否则它将无法正常工作:

  it('props should match mapDispatchToProps', () => {
    const wrapper = mount(
      <Provider store={store}>
        <IntlProvider locale="en" messages={{ en: {} }}>
          <MemoryRouter>
            <ConnectedToDos />
          </MemoryRouter>
        </IntlProvider>
      </Provider>,
      {},
    )

    wrapper.find('input[type="checkbox"]').first().simulate('change')
    wrapper.find('form').simulate('submit')
    wrapper.find('Icon[name="DELETE"]').first().simulate('click')

    const actions = store.getActions()
    const todosActions = actions.filter(action => action.type.includes('/todos/'))

    expect(todosActions).toHaveLength(4)
  })

我认为这些不是最好的测试,但是它们确实测试了我关心的问题。如果我有更好的解决方案,请确保会更新此问题。