如何用Jest测试导入的store2函数?React组件测试覆盖率问题
解决Jest测试中store2的clearAll()未被调用及覆盖率问题
问题描述
React应用中编写单元测试时,LogOutForDisagreement组件内的store.clearAll()代码行始终无法被测试覆盖,Jest断言该方法被调用失败,测试覆盖率仅83.33%,缺失第19行(store.clearAll())的覆盖。
组件代码(LogOutForDisagreement.js)
import React from 'react'; import Form from 'react-bootstrap/Form'; import Button from 'react-bootstrap/Button'; import ClipboardClip from './../ClipboardClip'; const store = require('store2'); function LogOutForDisagreement(props) { const handleClick = (e) => { e.preventDefault(); props.onChange( props.props.user = { loggedIn: false, email: '', password: '', firstName: '' }, ); store.clearAll(); //sessionStorage.clear(); }; return ( <div id="logOutForDisagreementClipboard" className={props.className}> <Form className="form"> <ClipboardClip /> <div className="centeredPaper"> <p>Sorry, you must agree to our privacy policy to access the site. You must now log out and log back in to agree to the terms.</p> <Button id="logOutForDisagreement" type="" onClick={handleClick}> Log Out </Button> </div> </Form> </div> ); } export default LogOutForDisagreement;
原测试代码(LogOutForDisagreement.test.js)
import React from 'react'; import { render, fireEvent, cleanup } from '@testing-library/react'; import toJson from 'enzyme-to-json'; import LogOutForDisagreement from './LogOutForDisagreement'; const store = require('store2'); let props = { props: { user: { loggedIn: false, email: 'email@example.com', password: '0cc3dd4ad221f83270f876908064fa67d43407aa4bd580731b30c58c4fe2d8de00464107e5a143594068461af21ece9fb542e2f254b6f895eea6a6627f4f7c438a42fb18af7f539c4be2456661a5beec4a561f9443988182445b3952a5d9321a2d725b24b151f79a2806432c848b0b0c02576676e3a5c6f0661b4e318ca4f3e134d066808fab8fdd5322ed5cf5ad68aae43254a8fefdb69809c1bfdc07fe0365f38baa424d0c059c3a9fbff1f3525dae410740b9719929ef3f34235da519591f0410a08438132600fa802079b8d6f372f6dc439eb1b100aed28bb55cc3c6dc8982644940bd506278943fa8e430836cb874283e9f4438aac04a817f86bd1606036f03d196a211bdd91ac683d4ec63fcd503aa97b53d5c2571d39855b2be5f77be80a7f767271c8789aec26c66530c22387007c704f96b1a76a47e8e13fb263a0a6b24b2959495d34b47e03bcb95e8af13b555c5c403ec01427182afa1bae35ff81224b051fde7a61bd9044ed74042444a05e06186eedacc38c0128ff7d70c', firstName: 'FirstName' }, className: "logOutForDisagreement" } }; describe('Test the log out for disagreement button', () => { it('renders correctly enzyme and has onclick', async () => { console.log("This is the store at the begining after initial load: {" + Object.entries(sessionStorage).map(([key, val] = sessionItem) => { return `${key}: ${val}`}) + "}"); const { getByText } = render(<LogOutForDisagreement id="logOutForDisagreementClipboard" className={props.className} />); const spy = jest.spyOn(store, 'clearAll'); await fireEvent.click(getByText("Log Out")); console.log("This is in the store after the event fires: {" + Object.entries(sessionStorage).map(([key, val] = sessionItem) => { return `${key}: ${val}`}) + "}"); expect(spy).toHaveBeenCalled(); expect(toJson(getByText)).toMatchSnapshot(); //spy.mockRestore(); }); });
测试报错信息
Test the log out for disagreement button › renders correctly enzyme and has onclick expect(jest.fn()).toHaveBeenCalled() Expected number of calls: >= 1 Received number of calls: 0 37 | return `${key}: ${val}`}) + "}"); 38 | > 39 | expect(spy).toHaveBeenCalled(); | ^ 40 | 41 | expect(toJson(getByText)).toMatchSnapshot(); 42 | //spy.mockRestore(); ... --------------------------------------------|---------|----------|---------|---------|---------------------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s --------------------------------------------|---------|----------|---------|---------|-------------------- ... LogOutForDisagreement.js | 83.33 | 100 | 100 | 83.33 | 19
解决方案
1. 核心问题分析
- 未传入
onChangeprops:组件handleClick方法中调用了props.onChange,但测试时未传入该props,导致执行时抛出错误,中断后续代码(包括store.clearAll())的执行。 - Spy监听对象不一致:测试中本地导入的
store实例与组件内导入的实例可能并非同一对象,导致spy无法捕获调用。
2. 修改后的测试代码
import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import toJson from 'enzyme-to-json'; import LogOutForDisagreement from './LogOutForDisagreement'; // 模拟整个store2模块,确保组件使用的是模拟对象 jest.mock('store2', () => ({ clearAll: jest.fn() })); const store = require('store2'); describe('Test the log out for disagreement button', () => { it('renders correctly and triggers store.clearAll on click', () => { // 创建mock的onChange函数,避免执行报错 const mockOnChange = jest.fn(); const testProps = { className: "logOutForDisagreement", onChange: mockOnChange, props: { user: { loggedIn: true, email: 'email@example.com', firstName: 'FirstName' } } }; const { getByText } = render(<LogOutForDisagreement {...testProps} />); fireEvent.click(getByText("Log Out")); // 验证onChange被正确调用 expect(mockOnChange).toHaveBeenCalled(); // 验证store.clearAll被调用 expect(store.clearAll).toHaveBeenCalled(); expect(toJson(getByText("Log Out"))).toMatchSnapshot(); }); });
3. 可选:优化组件内的props处理
组件中直接修改props.props.user的写法违反React单向数据流原则,建议调整为:
props.onChange({ ...props.props, user: { loggedIn: false, email: '', password: '', firstName: '' } });
避免直接修改传入的props对象,提升组件健壮性。
内容的提问来源于stack exchange,提问作者Sandog
相关产品推荐
相关产品推荐

