缺少Jest依赖时的测试选择

4 min read
Zekari
测试工程实践Node.js简单性

依赖缺失不是问题

项目报错了。测试文件找不到Jest。

这不是错误。这是提醒:你不一定需要Jest。

大多数开发者的第一反应是:安装Jest。但安装一个依赖意味着引入整个生态系统。配置文件、转译器、Mock库、断言库。

有时候,停下来问一个问题更有价值:我真的需要这些吗?

测试框架解决了什么问题

Jest提供了很多东西。

断言库。 expect(a).toBe(b)assert.strictEqual(a, b) 读起来舒服。

Mock工具。 jest.mock() 可以拦截模块导入,创建假对象。

测试运行器。 自动发现测试文件,并行执行,报告结果。

生态系统。 插件、覆盖率工具、快照测试。

这些都有价值。但它们解决的是什么问题?

它们解决的是复杂性问题。 当项目足够大,测试足够多,手动管理变得困难。框架提供的是组织能力。

如果项目还不够大呢?如果测试只有几个呢?

框架的价值就不明显了。有时候甚至是负担。

💡 Click the maximize icon to view in fullscreen

Node.js 已经有测试工具

很多人不知道,Node.js 18+ 自带了测试运行器。

不需要安装任何东西。

创建测试文件:

// test/math.test.js
import { test } from 'node:test'
import assert from 'node:assert/strict'

test('addition works', () => {
  assert.strictEqual(1 + 1, 2)
})

test('subtraction works', () => {
  assert.strictEqual(5 - 3, 2)
})

运行测试:

node --test

就这么简单。

没有配置文件。没有 package.json 中的额外依赖。没有转译步骤。

Node.js 内置的 node:test 模块提供:

  • 测试发现 - 自动查找 *.test.js 文件
  • 并发执行 - 默认并行运行测试
  • 嵌套测试 - 支持 test() 嵌套组织
  • 钩子函数 - before(), after(), beforeEach(), afterEach()
  • 测试跳过 - test.skip()test.only()
  • Mock 支持 - mock.fn(), mock.method() 等基础 Mock
  • 覆盖率 - --experimental-test-coverage 标志

这些功能覆盖了 90% 的日常测试需求。

assert 模块的朴素力量

assert 模块看起来原始。

assert.strictEqual(actual, expected)
assert.deepStrictEqual(obj1, obj2)
assert.throws(() => func(), Error)

没有语法糖。没有链式调用。没有自定义匹配器。

但这就是它的优势。

没有魔法。 你清楚知道每个断言在做什么。

没有抽象。 不需要学习框架的DSL。

没有版本问题。 assert 是标准库的一部分,永远兼容。

基础断言:

  • assert.strictEqual(a, b) - 严格相等(===
  • assert.deepStrictEqual(obj1, obj2) - 深度比较对象
  • assert.ok(value) - 真值检查

异常断言:

  • assert.throws(() => func()) - 期望抛出错误
  • assert.doesNotThrow(() => func()) - 期望不抛出错误
  • assert.rejects(async () => {}) - 异步函数抛出错误

否定断言:

  • assert.notStrictEqual(a, b) - 严格不相等
  • assert.notDeepStrictEqual(obj1, obj2) - 深度不相等

90% 的测试只需要这些。

简单方法的实践

假设你有一个简单的工具函数:

// src/utils/format.js
export function formatPrice(cents) {
  if (typeof cents !== 'number') {
    throw new TypeError('Price must be a number')
  }
  return `$${(cents / 100).toFixed(2)}`
}

用 Jest 测试:

// 需要安装: jest, @types/jest, babel-jest
// 需要配置: jest.config.js, .babelrc
import { formatPrice } from '../src/utils/format'

describe('formatPrice', () => {
  it('formats cents to dollars', () => {
    expect(formatPrice(1234)).toBe('$12.34')
  })

  it('throws on invalid input', () => {
    expect(() => formatPrice('invalid')).toThrow(TypeError)
  })
})

用 Node.js 内置工具测试:

// 需要安装: 无
// 需要配置: 无
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { formatPrice } from '../src/utils/format.js'

test('formatPrice converts cents to dollars', () => {
  assert.strictEqual(formatPrice(1234), '$12.34')
})

test('formatPrice throws on invalid input', () => {
  assert.throws(
    () => formatPrice('invalid'),
    { name: 'TypeError' }
  )
})

代码差不多长。但第二种方法:

  • 没有额外依赖
  • 没有配置文件
  • 启动速度更快
  • 更容易调试(没有转译层)

💡 Click the maximize icon to view in fullscreen

什么时候简单方法不够

简单方法有局限。

缺少复杂的 Mock。 node:test 的 Mock 功能基础。如果需要拦截模块导入或复杂的依赖注入,简单方法会很吃力。

缺少快照测试。 如果你需要测试大型对象或UI组件的输出,Jest的快照功能很有用。

缺少成熟的生态。 没有现成的插件、匹配器扩展、报告工具。

团队习惯。 如果团队已经习惯Jest,切换到简单方法有学习成本。

但这些局限只在特定场景下成立。

考虑使用 Jest/Vitest 当:

  • 项目有数百个测试文件
  • 需要复杂的 Mock 和依赖替换
  • 团队已投入到特定框架生态
  • 需要快照测试或并行测试优化
  • 使用了需要特殊处理的工具(TypeScript、JSX、CSS modules)

简单方法的价值在小型项目、工具库、CLI工具中最明显。

依赖是负债

每个依赖都是负债。

它需要维护。需要更新。可能引入安全漏洞。可能与其他依赖冲突。

Jest本身有几十个依赖。安装Jest意味着安装整个依赖树。

npm install jest
# 安装了 300+ 个包

这些包都需要被信任。都可能出问题。

如果你能用零依赖的方案达到同样的目标,为什么不呢?

这不是反对Jest。Jest在它适用的场景下非常强大。

这是在提醒:不要自动化地添加依赖。每个依赖都应该有明确的理由。

简单性是一种选择

简单不是妥协。

选择简单的测试工具不是因为不知道更好的工具存在。而是因为在当前场景下,简单的工具更合适。

更少的配置意味着更少的维护。更少的依赖意味着更少的风险。更直接的代码意味着更容易理解。

这些优势在小项目中被放大。一个100行的工具库不需要企业级的测试框架。

1. 确保 Node.js 版本 >= 18

node --version  # 应该 >= 18.0.0

2. 创建测试文件

// test/example.test.js
import { test } from 'node:test'
import assert from 'node:assert/strict'

test('example test', () => {
  assert.strictEqual(1 + 1, 2)
})

3. 运行测试

node --test

4. 添加到 package.json

{
  "scripts": {
    "test": "node --test"
  }
}

就这么简单。无需任何依赖。

最后

缺少Jest依赖不是问题。

它是机会。让你重新思考测试需要什么。

可能你需要Jest的全部能力。那就安装它。

可能你只需要验证几个函数的行为。那Node.js内置的工具已经足够。

测试的目标不是使用最流行的工具。是验证代码按预期工作。

有时候,最简单的方法就是最好的方法。


  • 你的项目中有多少测试真的需要 Jest 的高级特性?
  • 如果从零开始,你会选择框架还是内置工具?
  • 测试框架的便利性是否值得它带来的复杂性?
  • 简单性在什么时候会成为限制?

相关文章

使用Jest或Vitest作为测试框架有什么区别?

在Claude Code中写单元测试:简单高效的实践

参考资源

Related Posts

Articles you might also find interesting

用 AI Agents 加速测试环境配置

3 min read

测试环境的配置是重复的琐事。环境变量、测试数据库、配置文件——这些步骤消耗时间但不产生直接价值。AI agents 改变了这个等式。

Claude Code测试
Read More

在Claude Code中写单元测试:简单高效的实践

2 min read

测试不是负担,是对话。Claude Code改变了测试的成本结构,让测试回归本质:验证行为,而非追求覆盖率。

Claude Code测试
Read More

使用Jest或Vitest作为测试框架有什么区别?

1 min read

测试框架的选择不是功能列表的比较,而是关于工具哲学的选择。Jest代表完整性,Vitest代表原生性。

测试工程实践
Read More

API 测试各种边界情况

2 min read

边界情况是系统最脆弱的地方,也是最容易被忽略的地方。测试边界情况不是为了追求完美,而是为了理解系统的真实边界。

API测试
Read More

端到端 Postback 模拟测试

2 min read

真实的测试不是模拟完美的流程,而是重现真实世界的混乱。Postback 测试的价值在于发现系统在不确定性中的表现。

测试API
Read More

Git Hooks 驱动的文档同步

2 min read

文档不会自动更新,除非你让它自动更新。Git Hooks 是最接近代码变更的触发点,也是对抗文档腐烂最有效的位置。

Git自动化
Read More

分层修复

3 min read

生产问题没有银弹。P0 止血,P1 加固,P2 优化。优先级不是排序,而是在不确定性下的决策框架。

工程实践问题修复
Read More

查询先于假设

3 min read

数据库迁移后,所有功能失效。问题不在迁移本身,而在假设。真相只存在于查询结果中。

数据库迁移
Read More