Jest+react-testing-library中mock getElementsByClassName提升覆盖率
问题根因
你原来的测试代码不生效是三个错误导致的:
- 语法完全错误:
document.getElementsByClassName() = jest.fn(...)是给函数执行后的返回值赋值,属于非法操作,根本修改不了getElementsByClassName这个方法本身 - 就算改对mock写法,如果不等待
useEffect执行完成就做断言,也会拿不到执行结果 - 完全没必要mock:组件本身渲染就会输出6个class为
heading的h1标签,正常挂载后DOM里本来就有对应元素,强行mock原生DOM API容易和jsdom的真实行为冲突。
推荐方案:不Mock原生API,直接验证真实行为
react-testing-library的设计思路就是尽量模拟用户真实使用场景,不要随便mock浏览器原生能力,直接渲染组件后验证逻辑执行结果即可,覆盖率会正常统计,测试结果也最可靠:
import React from 'react'; import { render, screen } from '@testing-library/react'; import App from './App' afterEach(() => { jest.clearAllMocks(); }); it('组件正常渲染且useEffect逻辑正确执行', () => { // 监听console.log,验证分支判断逻辑 const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); render(<App />); // 验证组件挂载成功 expect(screen.getByText('1')).toBeInTheDocument(); // 验证非空分支触发,打印了预期日志 expect(consoleSpy).toHaveBeenCalledWith('DO SOMETHING !!'); // 验证所有heading元素都被设置了正确的id const headingList = document.getElementsByClassName('heading'); headingList.forEach(item => { expect(item.getAttribute('id')).toBe('header-toggle'); }); consoleSpy.mockRestore(); });
跑这个用例的时候,useEffect里的所有分支、循环逻辑都会被真实执行,代码覆盖率工具可以正常采集到执行数据。
特殊场景下的Mock正确写法
如果你确实有特殊需求必须mockgetElementsByClassName方法,按下面的写法实现,注意要在render前完成mock,测试结束后还原原生方法避免污染其他用例:
import React from 'react'; import { render } from '@testing-library/react'; import App from './App' afterEach(() => { jest.restoreAllMocks(); }); it('mock场景下验证useEffect逻辑', () => { const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); // 构造mock的元素集合 const mockElements = Array(6).fill(null).map(() => ({ setAttribute: jest.fn() })); // 正确mock原生方法,注意是spyOn document上的方法,返回mock数据 jest.spyOn(document, 'getElementsByClassName').mockReturnValue(mockElements); render(<App />); // 验证查询方法被正确调用 expect(document.getElementsByClassName).toHaveBeenCalledWith('heading'); // 验证日志打印 expect(consoleSpy).toHaveBeenCalledWith('DO SOMETHING !!'); // 验证每个元素的setAttribute都被正确执行 mockElements.forEach(el => { expect(el.setAttribute).toHaveBeenCalledWith('id', 'header-toggle'); }); consoleSpy.mockRestore(); });
注意:非必要不要用这种mock方案,因为绕过了真实DOM渲染逻辑,测试结果可能和线上实际运行情况不一致。
内容的提问来源于stack exchange,提问作者Hithesh kumar
相关产品推荐
相关产品推荐

