Node.js 测试报告格式一览和自定义 json 格式 `--test-reporter=spec | tap | dot | junit | lcov`

最近做了一个 SSE 接口 mock 工具 sse-stuntman 的时候用到 Node.js 内置的单元测试 runner 和覆盖率。故有此文。

github.com/legend80s/s...

下面是 Node.js 官方关于可支持报告格式的说明:

支持以下内置报告器:

  • specspec 报告器以易读的人性化格式输出测试结果,为默认报告器。
  • taptap 报告器以 TAP 格式输出测试结果。
  • dotdot 报告器采用极简紧凑格式输出测试结果:测试通过显示 .,测试失败显示 X
  • junit:junit 报告器以 jUnit XML 格式输出测试结果。
  • lcov:搭配 --experimental-test-coverage 参数使用时,lcov 报告器可输出测试覆盖率数据。

本文主要是看看各种格式长什么样,以便大家选择。

1. --test-reporter=spec #默认

md 复制代码
ℹ tests 78
ℹ suites 29
ℹ pass 78
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 3257.1436
ℹ start of coverage report
ℹ -----------------------------------------------------------------------------------------------------
ℹ file                           | line % | branch % | funcs % | uncovered lines
ℹ -----------------------------------------------------------------------------------------------------
ℹ src                            |        |          |         | 
ℹ  commands                      |        |          |         | 
ℹ   create-scenario.mjs          |  73.60 |    85.71 |   75.00 | 86-87 95-125
ℹ  config-loader.mjs             |  93.92 |    64.71 |  100.00 | 102-106 142-143 145-146
ℹ  openai-stream.mjs             |  91.07 |    95.45 |  100.00 | 51-70
ℹ  utils                         |        |          |         | 
ℹ   providers                    |        |          |         | 
ℹ    anthropic                   |        |          |         | 
ℹ     event-generator.mjs        |  99.10 |    83.33 |   88.89 | 90
ℹ     stream.mjs                 |  96.02 |    90.91 |  100.00 | 179-182 184-187
ℹ   server.mjs                   | 100.00 |    93.75 |  100.00 | 
ℹ   string.mjs                   | 100.00 |   100.00 |  100.00 | 
ℹ   token.mjs                    | 100.00 |   100.00 |  100.00 | 
ℹ -----------------------------------------------------------------------------------------------------
ℹ all files                      |  95.80 |    91.31 |   95.60 | 
ℹ -----------------------------------------------------------------------------------------------------
ℹ end of coverage report

2. --test-reporter=tap

bash 复制代码
ok 7 - anthropic-stream
  ---
  duration_ms: 136.9186
  type: 'suite'
  ...
1..7
# tests 79
# suites 31
# pass 79
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3501.0162
# start of coverage report
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
# file                    | line % | branch % | funcs % | uncovered lines
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
# src                     |        |          |         | 
#  commands               |        |          |         | 
#   create-scenario.mjs   |  73.60 |    85.71 |   75.00 | 86-87 95-125
#  config-loader.mjs      |  93.92 |    64.71 |  100.00 | 102-106 142-143 145-146
#  openai-stream.mjs      |  90.95 |    88.00 |  100.00 | 54-74
#  utils                  |        |          |         | 
#   providers             |        |          |         | 
#    anthropic            |        |          |         | 
#     event-generator.mjs |  99.10 |    83.33 |   88.89 | 90
#     stream.mjs          |  96.15 |    84.00 |  100.00 | 186-189 191-194
#   server.mjs            | 100.00 |    95.65 |   85.71 | 
#   string.mjs            | 100.00 |   100.00 |  100.00 | 
#   token.mjs             | 100.00 |   100.00 |  100.00 | 
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------# all files               |  87.95 |    73.93 |   85.56 | 
# ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------# end of coverage report

3. --test-reporter=dot

css 复制代码
❯ node --test --experimental-test-coverage --test-coverage-exclude="src/**/*.test.mjs" --test-reporter=dot

因为我的测试文件都是通过的,故都输出绿色点:

..............................................................................................................

4. --test-reporter=junit

sh 复制代码
❯ node --test --experimental-test-coverage --test-coverage-exclude="src/**/*.test.mjs" --test-reporter=junit    
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<testsuites>
        <testsuite name="anthropic-stream" time="0.125695" disabled="0" errors="0" tests="3" failures="0" skipped="0" hostname="PC-LEGEND80S">
                <testsuite name="writeAnthropicStream()" time="0.123685" disabled="0" errors="0" tests="8" failures="0" skipped="0" hostname="PC-LEGEND80S">
                        <testcase name="should emit message_start event first" time="0.003565" classname="test"/>
                        <testcase name="should honor delay multiplier" time="0.117796" classname="test"/>
                        <testcase name="should use custom model name" time="0.000357" classname="test"/>
                </testsuite>
                <testsuite name="writeAnthropicErrorStream()" time="0.000812" disabled="0" errors="0" tests="3" failures="0" skipped="0" hostname="PC-LEGEND80S">
                        <testcase name="should emit error SSE event for rate-limit" time="0.000196" classname="test"/>
                        <testcase name="should emit error SSE event for content-filter" time="0.000335" classname="test"/>
                        <testcase name="should emit error SSE event for server-error" time="0.000163" classname="test"/>
                </testsuite>
                <testsuite name="writeAnthropicNonStreamingResponse()" time="0.000735" disabled="0" errors="0" tests="3" failures="0" skipped="0" hostname="PC-LEGEND80S">
                        <testcase name="should return JSON with Anthropic Messages API format" time="0.000283" classname="test"/>
                        <testcase name="should include usage statistics" time="0.000204" classname="test"/>
                        <testcase name="should contain valid message id" time="0.000143" classname="test"/>
                </testsuite>
        </testsuite>
        <!-- tests 79 -->
        <!-- suites 31 -->
        <!-- pass 79 -->
        <!-- fail 0 -->
        <!-- cancelled 0 -->
        <!-- skipped 0 -->
        <!-- todo 0 -->
        <!-- duration_ms 3529.7627 -->
</testsuites>

5. --test-reporter=lcov

sh 复制代码
❯ node --test --experimental-test-coverage --test-coverage-exclude="src/**/*.test.mjs" --test-reporter=lcov
ts 复制代码
SF:src\utils\token.mjs
FN:22,calculateTokens
FN:25,anonymous_1
FNDA:41,calculateTokens
FNDA:2666,anonymous_1
FNF:2
FNH:2
BRDA:1,0,0,3
BRDA:22,1,0,41
BRDA:25,2,0,2666
BRDA:25,3,0,1041
BRF:4
BRH:4
DA:1,3
DA:2,3
<...省略>
DA:25,41
DA:26,41
DA:27,41
DA:28,41
DA:29,41
DA:30,41
DA:31,41
LH:31
LF:31
end_of_record

6. 自定义

我想输出成 json 格式,这样方便 git diff,那就需要自定义 reporter 了。

把官方示例改一下就行,先看输出效果:

js 复制代码
❯ node --test --experimental-test-coverage --test-coverage-exclude="src/**/*.test.mjs" --test-reporter ./scripts/json-reporter.mjs src/scenario-parser.test.mjs
ts 复制代码
tests 13
suites 4
pass 13
fail 0
cancelled 0
skipped 0
todo 0
duration_ms 269.0277

{
  "totalLineCount": 731,
  "totalBranchCount": 41,
  "totalFunctionCount": 20,
  "coveredLineCount": 438,
  "coveredBranchCount": 32,
  "coveredFunctionCount": 10,
  "coveredLinePercent": 59.91792065663475,
  "coveredBranchPercent": 78.04878048780488,
  "coveredFunctionPercent": 50
}

官方示例 nodejs.org/docs/latest... 修改如下:

js 复制代码
// scripts/json-reporter.mjs
export default async function* customReporter(source) {
  for await (const event of source) {
    switch (event.type) {
      case "test:watch:drained":
        yield "test watch queue drained\n"
        break
      case "test:watch:restarted":
        yield "test watch restarted due to file change\n"
        break

      case "test:fail":
        yield `"${event.data.name}" 🔴\n`
        break

      case "test:diagnostic":
      case "test:stderr":
      case "test:stdout":
        yield `${event.data.message}\n`
        break
      case "test:coverage": {
        yield `\n${JSON.stringify(event.data.summary.totals, null, 2)}\n`
        break
      }
    }
  }
}

共有 9 种 event.type,这些事件类型反映了 Node.js 内置测试框架的生命周期:

  • test:enqueue --- 测试入队(等待执行)
  • test:dequeue --- 测试出队(开始执行)
  • test:start --- 测试开始运行
  • test:pass --- 测试通过
  • test:complete --- 测试完成
  • test:plan --- 测试计划(确定要运行的测试集)
  • test:diagnostic --- 诊断信息(如 tests/suites/pass/fail 等统计)
  • test:summary --- 测试摘要
  • test:coverage --- 覆盖率报告
相关推荐
陳陈陳1 天前
🚀 前端流式输出革命:SSE + BFF 架构从零到一实战指南
vue.js·架构·node.js
Revolution611 天前
第一次运行 Node.js:终端里的 JavaScript 怎样执行
后端·面试·node.js
半句唐诗1 天前
我是如何通过 Access Token 成功发布第一个 npm 包的
前端·npm·node.js
贩卖黄昏的熊1 天前
NestJS简明教程——异常处理和日志
javascript·node.js·nest.js
Revolution611 天前
Node.js 是什么:前端项目里哪些事情由它完成
前端·面试·node.js
Revolution611 天前
Nest.js 是什么:怎样用它写出第一个后端接口
后端·node.js·nestjs
柳林林2 天前
解决 nvm 切换 Node.js 版本时报“没有文件扩展 ‘.vbs’ 的脚本引擎”错误
node.js
To_OC2 天前
从 0 到 1:Milvus + 大模型打造私人记忆知识库
人工智能·node.js·llm
孟陬2 天前
介绍 Node.js v22 新增的 `toArray` 方法
node.js·deno·bun