Jest代码覆盖率统计异常:空行、注释行等被标记为未覆盖
解决Jest错误标记空行/注释行等为未覆盖的问题
环境依赖
"jest": "^29.5.0", "jest-fetch-mock": "^3.0.3", "jest-sonar-reporter": "^2.0.0",
Jest配置文件(jest.config.js)
module.exports = { automock: false, clearMocks: true, collectCoverage: true, coverageDirectory: "<rootDir>/Test/Reports", coveragePathIgnorePatterns: ["/node_modules/", "/Test/Unit/"], coverageReporters: ["text", ["lcovonly", { projectRoot: "." }]], testEnvironment: "node", testResultsProcessor: "<rootDir>/Test/node_modules/jest-sonar-reporter", testEnvironmentOptions: { browsers: ["chrome", "firefox", "safari"], url: "http://localhost", }, moduleNameMapper: { "\\.(css|less|sass|scss)$": "<rootDir>/Test/__mocks__/styleMock.js", "\\.(gif|ttf|eot|png|svg)$": "<rootDir>/Test/__mocks__/fileMock.js", "^react$": "<rootDir>/Test/node_modules/react", "^react-dom$": "<rootDir>/Test/node_modules/react-dom", }, rootDir: "../", roots: ["<rootDir>"], setupFiles: ["<rootDir>/Test/node_modules/mock-local-storage"], setupFilesAfterEnv: [ "<rootDir>/Test/Unit/setupJest.js", "<rootDir>/Test/node_modules/mock-local-storage", ], transform: { "^.+\\.(js|jsx|mjs)$": "<rootDir>/Test/jest-transformer.js", }, verbose: true, watchman: true, };
问题现象
生成的覆盖率汇总表和lcov.info文件错误地将空行、注释行、已初始化变量、解构属性甚至不存在的行标记为未覆盖(例如:500行的文件,报告显示700-800行未覆盖)。代码编辑器侧边栏的覆盖率标记也存在类似错误。
源代码示例
export default function formatArray(unformattedArray) { const removingNewLines = unformattedArray.toString().split(",\n"); const stringArray = removingNewLines.toString().split(","); const trimmedArray = stringArray.map((s) => s.trim()); trimmedArray.forEach((elem, i) => { if (elem.includes("\n")) { trimmedArray.splice(i, 1); trimmedArray.push(elem.replace(/\n/g, "")); } }); const arrLen = trimmedArray.length; if (trimmedArray[arrLen - 1] === "") { console.log("Reached?"); trimmedArray.pop(); } return trimmedArray; }
测试用例示例
import formatArray from "./formatArray"; describe("formatArray test", () => { const expectedResp = ["http://localhost:3000/about", "http://localhost:4000/about"]; it("with single string", async () => { const arrayResp = ["http://localhost:3000/about"]; let resp = formatArray(arrayResp); expect(resp).toEqual(arrayResp); }); it("with new line in string", async () => { const arrayResp = ["http://localhost:3000/about,\nhttp://localhost:4000/about"]; let resp = formatArray(arrayResp); expect(resp).toEqual(expectedResp); }); it("with space in string", async () => { const arrayResp = ["http://localhost:3000/about, http://localhost:4000/about"]; let resp = formatArray(arrayResp); expect(resp).toEqual(expectedResp); }); it("with comma towards end in string", async () => { const arrayResp = ["http://localhost:3000/about,http://localhost:4000/about,"]; let resp = formatArray(arrayResp); expect(resp).toEqual(expectedResp); }); it("with space towards end in string", async () => { const arrayResp = ["http://localhost:3000/about,http://localhost:4000/about "]; let resp = formatArray(arrayResp); expect(resp).toEqual(expectedResp); }); });
覆盖率结果
执行测试脚本test: "node --unhandled-rejections=none node_modules/jest/bin/jest.js --updateSnapshot"后,覆盖率汇总如下:
console.log Reached? at formatArray (Source/formatArray.js:404:13) PASS Unit/formatArray.test.js (13.036 s) formatArray test √ with single string (15 ms) √ with new line in string (1 ms) √ with space in string (1 ms) √ with comma towards end in string (111 ms) √ with space towards end in string (2 ms) ----------------|---------|----------|---------|---------|------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ----------------|---------|----------|---------|---------|------------------- All files | 86.66 | 75 | 100 | 85.71 | formatArray.js | 86.66 | 75 | 100 | 85.71 | 15-16 ----------------|---------|----------|---------|---------|------------------- Test Suites: 1 passed, 1 total Tests: 5 passed, 5 total Snapshots: 0 total Time: 13.602 s Ran all test suites matching /formatArray.test.js/i.
解决方案
1. 检查自定义转换器的源映射配置
Jest的覆盖率计算依赖转换器输出的源映射,如果自定义jest-transformer.js没有正确生成源映射,会导致行号匹配错误:
- 优先使用
babel-jest作为基础转换器,避免完全自定义转换逻辑 - 在Babel配置中设置
sourceMaps: "both"或inline,确保生成完整的源映射 - 如果必须自定义转换函数,返回结果必须包含
code和map属性,保证源映射关联原始代码与转换后的代码
2. 修复jest-sonar-reporter兼容性问题
旧版本jest-sonar-reporter与Jest 29.x存在行号解析bug:
- 暂时移除
testResultsProcessor配置,重新运行测试验证覆盖率是否正常 - 若需要Sonar集成,升级
jest-sonar-reporter到最新版本,或改用官方维护的jest-sonar包
3. 修正rootDir路径配置
rootDir: "../"可能导致路径解析混乱,影响源映射匹配:
- 将
rootDir调整为项目根目录(如./),同步修改coverageDirectory、moduleNameMapper等路径配置 - 确保所有路径引用都基于正确的根目录
4. 统一文件换行符与编码
不同操作系统的换行符(CRLF/LF)会导致Jest行号解析错误:
- 将项目所有文件的换行符统一为LF(Unix风格)
- 确保源文件与测试文件编码一致(推荐UTF-8)
5. 临时禁用watchman排查缓存问题
watchman的文件缓存可能导致Jest读取的文件版本与实际不一致:
- 在
jest.config.js中设置watchman: false,重新运行测试看问题是否解决
6. 升级Jest到稳定版本
Jest 29.5.0存在已知的覆盖率计算bug,升级到最新29.x稳定版可修复部分问题:
npm install jest@latest --save-dev
内容的提问来源于stack exchange,提问作者ayeshas7
相关产品推荐
相关产品推荐

