使用Jest测试React类组件时如何Mock常量options.isShow
问题:无法Mock
options.isShow 为true导致组件render返回null options.isShow始终为false,导致render()仅返回null,尝试Mock该值为true但无效
组件代码(a.js)
// a.js import React, {Component} from 'react'; import merge from 'lodash-es/merge'; export let options = { isShow: false, //.... }; export default class Button extends Component { constructor(props) { super(props); } renderBtn() { //... } render() { const {data = {}} = this.props; // console.log(options.isShow) // false merge(options, data); return options.isShow ? this.renderBtn() : null; } }
最初的测试代码(a.spec.js)
// a.spec.js(最初版本) import Button from 'a.js'; jest.mock('lodash-es/merge', () => jest.fn(fn => fn)); describe('Button', () => { it('a', done => { const ButtonI = new Button({ data: {}, }); ButtonI.renderBtn = jest.fn(); ButtonI.render(); expect(ButtonI.renderBtn).toBeCalled(); done(); }); })
尝试的无效Mock方式
直接在测试文件中替换options对象,测试文件控制台显示options.isShow为true,但组件内部的options.isShow仍为false:
// a.spec.js(尝试修改后的版本) import * as ButtonAll from 'a.js'; jest.mock('lodash-es/merge', () => jest.fn(fn => fn)); describe('Button', () => { it('a', done => { const Button = ButtonAll.default; ButtonAll.options = { isShow: true, // .... } // console.log(ButtonAll.options.isShow) // true const ButtonI = new Button({ data: {}, }); ButtonI.renderBtn = jest.fn(); ButtonI.render(); expect(ButtonI.renderBtn).toBeCalled(); done(); }); })
解决方案
问题核心是Mock逻辑错误,以及直接替换options引用无法同步到组件内部变量,以下两种方式可以解决:
方式一:正确Mock merge并传入含isShow: true的props
之前Mock的merge仅返回第一个参数,未执行合并逻辑。修改Mock逻辑让其真正合并属性,同时传入带isShow: true的data:
// a.spec.js import Button from './a.js'; import * as ButtonAll from './a.js'; // 让Mock的merge执行真实的对象合并逻辑 jest.mock('lodash-es/merge', () => jest.fn((target, source) => Object.assign(target, source)) ); describe('Button', () => { it('should call renderBtn when isShow is true', () => { // 重置options状态,避免测试污染 ButtonAll.options.isShow = false; const ButtonI = new Button({ data: { isShow: true }, }); ButtonI.renderBtn = jest.fn(); ButtonI.render(); expect(ButtonI.renderBtn).toHaveBeenCalled(); }); })
方式二:修改原options对象的属性(而非替换整个对象)
组件内部的options绑定了模块加载时的原对象引用,直接替换ButtonAll.options不会改变组件内部的变量指向,需修改原对象的属性:
// a.spec.js import * as ButtonAll from './a.js'; jest.mock('lodash-es/merge', () => jest.fn(fn => fn)); describe('Button', () => { it('should call renderBtn when isShow is true', () => { // 修改原对象的isShow属性,而非替换整个options ButtonAll.options.isShow = true; const Button = ButtonAll.default; const ButtonI = new Button({ data: {}, }); ButtonI.renderBtn = jest.fn(); ButtonI.render(); expect(ButtonI.renderBtn).toHaveBeenCalled(); // 测试后重置状态,避免影响其他用例 ButtonAll.options.isShow = false; }); })
补充说明
- 直接替换
ButtonAll.options无效的原因:组件内部的options变量在模块加载时就绑定了原对象的引用,测试中替换导出的options只是改变了外部引用,组件内部仍指向原对象。 - 测试后务必重置
options状态,防止不同测试用例之间互相干扰。
内容的提问来源于stack exchange,提问作者asui
相关产品推荐
相关产品推荐

