缺少Jest依赖时的测试选择
依赖缺失不是问题
项目报错了。测试文件找不到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 的高级特性?
- 如果从零开始,你会选择框架还是内置工具?
- 测试框架的便利性是否值得它带来的复杂性?
- 简单性在什么时候会成为限制?
相关文章
参考资源
Related Posts
Articles you might also find interesting
用 AI Agents 加速测试环境配置
测试环境的配置是重复的琐事。环境变量、测试数据库、配置文件——这些步骤消耗时间但不产生直接价值。AI agents 改变了这个等式。
在Claude Code中写单元测试:简单高效的实践
测试不是负担,是对话。Claude Code改变了测试的成本结构,让测试回归本质:验证行为,而非追求覆盖率。
使用Jest或Vitest作为测试框架有什么区别?
测试框架的选择不是功能列表的比较,而是关于工具哲学的选择。Jest代表完整性,Vitest代表原生性。
API 测试各种边界情况
边界情况是系统最脆弱的地方,也是最容易被忽略的地方。测试边界情况不是为了追求完美,而是为了理解系统的真实边界。
端到端 Postback 模拟测试
真实的测试不是模拟完美的流程,而是重现真实世界的混乱。Postback 测试的价值在于发现系统在不确定性中的表现。
Git Hooks 驱动的文档同步
文档不会自动更新,除非你让它自动更新。Git Hooks 是最接近代码变更的触发点,也是对抗文档腐烂最有效的位置。
分层修复
生产问题没有银弹。P0 止血,P1 加固,P2 优化。优先级不是排序,而是在不确定性下的决策框架。
查询先于假设
数据库迁移后,所有功能失效。问题不在迁移本身,而在假设。真相只存在于查询结果中。