javascript - 模拟由 Redux Provider 组件封装的 React 组件函数
问题描述
这是我的情况:我正在尝试对一个 React 组件 (TodoList) 进行单元测试,该组件在 Render 方法上只执行映射项目并显示它们。它使用 MapStateToProps 从 Redux 存储中获取项目(TodoItem)。
这是 TodoList 组件的 javascript 代码:
class TodoList extends React.Component {
onRemoveClick = (id) => {
diContainer.dataLayer.todo.remove(id);
}
render() {
const todos = this.props.todos;
if(!todos)
return null;
return (
todos.map(todo => (
<TodoItem key={todo.id} todo={todo} onRemove={this.onRemoveClick} />
))
);
}
}
const mapStateToProps = (state) => ({
todos: state.todos
});
export default connect(mapStateToProps)(TodoList);
我现在要测试的是,每当调用 TodoItem(子对象)内的按钮时,都会调用 onRemoveClick 委托方法。
我尝试将 Jest 提供的模拟功能与 Enzyme 结合使用。但是,因为 TodoList 从 Redux 获取他的数据,所以我必须用 Provider 组件包围我的 Enzyme mount() 调用并模拟存储。
这是我的测试代码:
import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import TodoList from '../../../../react/components/todo/TodoList';
describe('TodoList component', () => {
//global arrange
const storeState = {
todos: [
{
id: 'testId1',
title: 'testTitle1',
description: 'testDescription1'
},
{
id: 'testId2',
title: 'testTitle2',
description: 'testDescription2'
},
]
};
const mockStore = configureStore();
let store;
beforeEach(() => {
store = mockStore(storeState)
});
it('Removes a todo from the list if the remove button the todo was pressed', () => {
//arrange
//let mockFn = jest.fn();
//TodoList.prototype.onRemoveClick = mockFn; => tried, doesn't work...
const component = mount(
<Provider store={store}>
<TodoList />
</Provider>
);
//component.instance() => tried working with this, but couldn't find it
//component.instance().children() => is not the TodoList
const items = component.find('.todoItem');
//act
const button = items.first().find('button');
button.simulate('click');
//assert
//Todo: check function spy here
});
});
我注释掉了一些我尝试过的东西。但是我似乎无法在我的测试代码中访问 TodoList 组件,因为 Provider 包装器......
有什么线索吗?
解决方案
通过大量的试验和错误得到它修复。我能够通过酶查找功能访问 TodoList,这显然也适用于 ComponentNames(而不仅仅是普通的 HTML 选择器)。
第二个技巧是在 TodoList 组件上调用 forceUpdate() 并在父包装器上调用 update()。
it('Removes a todo from the list if the remove button on the todo was pressed', () => {
//arrange
const component = mount(
<Provider store={store}>
<TodoList />
</Provider>
);
const list = component.find('TodoList').instance();
let mockFn = jest.fn();
list.onRemoveClick = mockFn;
list.forceUpdate();
component.update();
//act
const items = component.find('.todoItem');
const button = items.first().find('button');
button.simulate('click');
//assert
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(1);
expect(mockFn).toHaveBeenCalledWith('testId1');
});
推荐阅读
- android - 打开whatsapp //:在webview中
- javascript - 非常基本的 React + NodeJS 获取:为什么我的响应不是我想要的?
- xcode - 无法按顺序对 SwiftUI 图像进行动画处理或过渡
- c++ - (第 1 位 + 第 2 位)十进制数
- javascript - JS通过引用推送对象的原因是什么?
- java - JavaFX ListView 使用自定义组件但有多个列?
- vue.js - 如何在 100% 完成的 vuetify 项目上实现服务器端渲染(ssr)?
- python - 迭代 Python 列表并删除每个子列表的最终索引,无导入
- sql-server - 使用 SSIS 从经过身份验证的 url 下载文件时出错
- java - 我可以用什么来模拟无穷大?