Skip to content

来了解下单元测试吧。

单元测试是测试中的一个重要环节,它针对软件中的最小可测试单元进行验证,通常是指对代码中的单个函数、方法或模块进行测试。单元测试旨在确定特定部分代码的行为是否符合预期,通过针对单元代码的各种输入情况进行测试,来验证代码的正确性、稳定性和可靠性。

还会有一个概念叫集成测试,集成测试是将多个模块组合在一起,验证他们的相互协作是否正确。这种测试更关注于模块的接口及其交互。

那为什么要做单元测试?对于前端单元测试可以帮助开发者:

  • 早期发现和修复问题,节省后续的修复成本。
  • 改善代码质量,使其更加健壮和可靠。
  • 为团队提供文档,帮助理解代码的预期行为。

在Node.js领域,有多个测试框架可供选择,以下是一些流行的选择:

  • Mocha:一个功能丰富的测试框架,支持多种风格的测试(行为驱动、测试驱动等),强大的断言库。
  • Jasmine:一个行为驱动的开发框架,简单易用,提供了丰富的断言和测试功能。
  • Jest:由Facebook开发的测试框架,具备快速的测试运行和优雅的API,内置对ES6、异步测试的支持。有模拟函数、快照等特色功能。
  • Chai:一个断言库,可以与Mocha等测试框架结合使用,支持多种断言风格。
  • Vitest:Vitest 与 Jest 兼容,具有开箱即用的 ESM、Typescript 和 JSX 支持,并且由 esbuild 提供支持。它在测试过程中使用 Vite 开发服务器来转换你的文件,并监听我们的应用程序的相同配置(通过 vite.config.js ),从而消除了使用Jest等测试替代品所涉及的重复工作。

Tips:test 测试 | Node.js v24 文档

NodeJS 从 16 开始自带测试模块,包括了node:testnode:assert相对于比较主流的 jest, mocha 等,好处是简单、轻量级,不需要安装额外的依赖,重要是速度很快!

接下来我们来看一下怎么使用这个模块进行单元测试。

参考资料:

  • index.ts
typescript
/**
 * @brief 根据输入数字返回字符串表示
 * @param input 输入的数字
 * @return 字符串结果:'Positive'(正数)、'Zero'(零)、'Error'(错误)
 * @throws 当输入为负数时抛出 Error('Negative')
 */
export function testPrettier(input: number): string {
  try {
    if (input > 0) {
      return 'Positive';
    } else if (input < 0) {
      throw new Error('Negative'); // 此行会爆出错误,中断程序执行
    }
    return 'Zero';
  } catch (err) {
    console.error(err);
    return 'Error';
  }
}

console.log("=============start===============");
const message: string = 'Hello, World!';
console.log(message);
console.log(testPrettier(5));
console.log(testPrettier(0));
console.log(testPrettier(-5));
console.log("=============end===============");
  • 测试文件
ts
import { describe, it } from 'node:test';
import assert from 'node:assert';
import { testPrettier } from '../src/index.ts';

describe('Test: base test', () => {
  it('测试正整数输入 (5)', () => {
    console.log('STEP: 验证输入5时返回"Positive"');
    assert.deepStrictEqual(
      testPrettier(5),
      'Positive'
    );
  });

  it('测试零输入 (0)', () => {
    console.log('STEP: 验证输入0时返回"Zero"');
    assert.deepStrictEqual(testPrettier(0), 'Zero');
  });

  it('测试负整数输入 (-5)', () => {
    console.log('STEP: 验证输入-5时返回"Error"');
    assert.deepStrictEqual(testPrettier(-5), 'Error');
  });
});

assert.deepStrictEqual函数原型:

typescript
assert.deepStrictEqual(actual, expected[, message])

该函数接受上述和以下描述的以下参数:

  • **actual: **此参数保存需要评估的实际值。它是任何类型。
  • **expected: **此参数保存与实际值匹配的期望值。它是任何类型。
  • **message: **此参数保存字符串或错误类型的错误消息。它是一个可选参数。

**返回值:**此函数返回对象类型的断言错误。

我们要使用node:test,还需要安装依赖:

shell
npm i -D @types/node

由于我们的测试文件是ts,ts文件还需要编译,我们这里可以安装tsx工具,直接运行ts文件:

shell
npm i -D tsx

json
"scripts": {
    "test": "tsx test/index.test.ts",
    //......
  },

然后我们就可以运行测试了。

shell
D:\sumu_blog\ts-demo [master ↑2 +1 ~3 -0 !]> npm run test

> ts-demo@1.0.0 test
> tsx test/index.test.ts

=============start===============
Hello, World!
Positive
Zero
Error: Negative
    at testPrettier (D:\sumu_blog\ts-demo\src\index.ts:22:13)
    at <anonymous> (D:\sumu_blog\ts-demo\src\index.ts:36:13)
    at Object.<anonymous> (D:\sumu_blog\ts-demo\src\index.ts:37:46)
    at Module._compile (node:internal/modules/cjs/loader:1730:14)
    at Object.transformer (D:\sumu_blog\ts-demo\node_modules\tsx\dist\register-D46fvsV_.cjs:3:1104)
    at Module.load (node:internal/modules/cjs/loader:1465:32)
    at Function._load (node:internal/modules/cjs/loader:1282:12)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:235:24)
    at Module.require (node:internal/modules/cjs/loader:1487:12)
Error
=============end===============
STEP: 验证输入5时返回"Positive"
STEP: 验证输入0时返回"Zero"
STEP: 验证输入-5时返回"Error"
Error: Negative
    at testPrettier (D:\sumu_blog\ts-demo\src\index.ts:22:13)
    at TestContext.<anonymous> (D:\sumu_blog\ts-demo\test\index.test.ts:21:28)
    at Test.runInAsyncScope (node:async_hooks:214:14)
    at Test.run (node:internal/test_runner/test:1047:25)
    at Suite.processPendingSubtests (node:internal/test_runner/test:744:18)
    at Test.postRun (node:internal/test_runner/test:1173:19)
    at Test.run (node:internal/test_runner/test:1101:12)
    at async Suite.processPendingSubtests (node:internal/test_runner/test:744:7)
 Test: base test
 测试正整数输入 (5) (1.096ms)
 测试零输入 (0) (0.3967ms)
 测试负整数输入 (-5) (0.774ms)
 Test: base test (3.331ms)
 tests 3
 suites 1
 pass 3
 fail 0
 cancelled 0
 skipped 0
 todo 0
 duration_ms 9.4593

Error: Negative这部分就是因为调用了testPrettier(-5),这个时候函数抛出了异常,然后被捕获,通过console.error(err);打印出来的。

Vitest(发音为 "veetest") 是由 Vite 驱动的下一代测试框架。详细的介绍直接看文档吧:为什么是 Vitest | 指南 | Vitest,它有什么特点?还是看文档吧,文档写的还是很详细的:主要功能 | 指南 | Vitest

shell
npm install -D vitest

莫道桑榆晚 为霞尚满天.