You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 17:07:35