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测试mapStateToProps
和mapDispatchToProps
?
答案 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)
})
我认为这些不是最好的测试,但是它们确实测试了我关心的问题。如果我有更好的解决方案,请确保会更新此问题。