React中用Jest和Enzyme测试componentWillMount内函数失败排查
解决Jest+Enzyme测试React容器组件时mock函数未被调用的问题
问题描述
你在测试AgentForm组件的componentWillMount生命周期时,发现mock的resetStatusMessage函数始终未被调用,测试失败。相关代码和错误信息如下:
React组件代码(AgentForm.js)
import { resetStatusMessage, formSetError, formResetError, createAgent, updateAgent } from "../../actions"; componentWillMount = () => { const { agents, match, push, resetStatusMessage, formResetError } = this.props; this.edit = false; this.isNewAvatar = false; this.agent = {}; if (match.params.id) { this.edit = true; this.agent = getAgent(agents, match.params.id); if ("undefined" === typeof this.agent) { push("/agents"); } } resetStatusMessage(); formResetError(); };
测试代码
import React from "react"; import ReactDOM from "react-dom"; import { Provider } from "react-redux"; import configureStore from "../../store/configureStore"; import AgentForm from "./index"; import { mount } from "enzyme"; it("should call resetStatusMessage when mounted", () => { let mockResetStatusMessage = jest.fn(), store = configureStore(); const wrapper = mount( <Provider store={store}> <AgentForm resetStatusMessage={mockResetStatusMessage} /> </Provider> ); expect(wrapper).toBeDefined(); expect(mockResetStatusMessage).toHaveBeenCalled(); });
错误信息
expect(jest.fn()).toHaveBeenCalled() Expected mock function to have been called
问题根源
你大概率是用react-redux的connect把AgentForm包装成了容器组件。这时候直接给AgentForm传resetStatusMessage props是无效的——外层的connect会覆盖你手动传入的props,组件实际接收到的是从redux store通过mapDispatchToProps映射过来的action函数,而非你定义的mock函数。
解决方案
这里提供两种可行的解决思路:
方案一:导出未被connect包裹的原始组件
在AgentForm.js中,除了默认导出被connect包裹的组件,额外导出原始的未被包裹的组件:
// AgentForm.js // 定义原始组件 class AgentForm extends React.Component { // ...你的组件逻辑代码 } // 命名导出原始组件,供测试使用 export { AgentForm }; // 默认导出被connect包裹的容器组件 export default connect(mapStateToProps, mapDispatchToProps)(AgentForm);
然后在测试文件中,导入这个命名导出的原始组件,直接测试它(无需Provider):
import React from "react"; import { mount } from "enzyme"; // 导入未被connect包裹的原始组件 import { AgentForm } from "./index"; it("should call resetStatusMessage when mounted", () => { const mockResetStatusMessage = jest.fn(); const mockFormResetError = jest.fn(); // 传入所有组件需要的mock props const wrapper = mount( <AgentForm resetStatusMessage={mockResetStatusMessage} formResetError={mockFormResetError} match={{ params: {} }} agents={[]} push={jest.fn()} /> ); expect(mockResetStatusMessage).toHaveBeenCalled(); expect(mockFormResetError).toHaveBeenCalled(); });
这种方法的优势是专注于组件本身的逻辑测试,无需关心redux的细节。
方案二:直接Mock action函数
如果你想测试被connect包裹的容器组件,可以直接mock整个actions模块:
import React from "react"; import { Provider } from "react-redux"; import configureStore from "../../store/configureStore"; import AgentForm from "./index"; import { mount } from "enzyme"; // 导入actions模块用于mock import * as actions from "../../actions"; // Mock整个actions模块的所有函数 jest.mock("../../actions", () => ({ resetStatusMessage: jest.fn(), formResetError: jest.fn(), createAgent: jest.fn(), updateAgent: jest.fn(), formSetError: jest.fn() })); it("should call resetStatusMessage when mounted", () => { const store = configureStore(); const wrapper = mount( <Provider store={store}> <AgentForm /> </Provider> ); expect(actions.resetStatusMessage).toHaveBeenCalled(); expect(actions.formResetError).toHaveBeenCalled(); });
这种方式会让组件调用mock后的action函数,从而可以验证函数是否被正确触发。
内容的提问来源于stack exchange,提问作者Khushi
相关产品推荐
相关产品推荐

