Jest 调试技巧 #
调试概述 #
测试调试是解决测试问题的重要技能,掌握调试技巧可以快速定位问题。
text
┌─────────────────────────────────────────────────────────────┐
│ 调试方法 │
├─────────────────────────────────────────────────────────────┤
│ 1. 日志输出 - console.log 等基本方法 │
│ 2. 断点调试 - 使用调试器暂停执行 │
│ 3. 错误分析 - 解读错误消息 │
│ 4. 工具集成 - VS Code 等编辑器集成 │
│ 5. 快照调试 - 查看测试快照 │
└─────────────────────────────────────────────────────────────┘
日志输出 #
console.log #
javascript
test('debug with console.log', () => {
const user = { name: 'John', age: 30 };
console.log('user:', user);
console.log('user.name:', user.name);
console.log('JSON:', JSON.stringify(user, null, 2));
expect(user.name).toBe('John');
});
console.table #
javascript
test('debug with console.table', () => {
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
];
console.table(users);
expect(users).toHaveLength(2);
});
console.dir #
javascript
test('debug with console.dir', () => {
const obj = { a: { b: { c: 1 } } };
console.dir(obj, { depth: null });
expect(obj.a.b.c).toBe(1);
});
使用 debug 包 #
javascript
import debug from 'debug';
const log = debug('test:user');
test('debug with debug package', () => {
const user = { name: 'John' };
log('user data: %o', user);
expect(user.name).toBe('John');
});
bash
# 启用 debug 输出
DEBUG=test:* jest
断点调试 #
Node 调试器 #
bash
# 启动调试模式
node --inspect-brk node_modules/.bin/jest --runInBand
# 或使用 node inspect
node --inspect node_modules/.bin/jest --runInBand --no-cache
Chrome DevTools #
bash
# 启动调试
node --inspect-brk node_modules/.bin/jest --runInBand
# 打开 Chrome
# chrome://inspect
# 点击 "Open dedicated DevTools for Node"
VS Code 调试 #
json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Jest Debug",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"--runInBand",
"--no-cache"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
},
{
"type": "node",
"request": "launch",
"name": "Jest Debug Current File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"${fileBasenameNoExtension}",
"--config",
"jest.config.js"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
调试特定测试 #
json
{
"type": "node",
"request": "launch",
"name": "Jest Debug Specific Test",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"--runInBand",
"--no-cache",
"--testNamePattern=should add user"
],
"console": "integratedTerminal"
}
错误分析 #
解读错误消息 #
javascript
// 典型错误消息
// ● Test suite failed to run
//
// TypeError: Cannot read property 'name' of undefined
//
// at Object.<anonymous> (user.test.js:5:18)
// at processTicksAndRejections (internal/process/task_queues.js:95:5)
// 分析步骤:
// 1. 错误类型: TypeError
// 2. 错误消息: Cannot read property 'name' of undefined
// 3. 错误位置: user.test.js:5:18
增强错误输出 #
javascript
test('better error messages', () => {
const result = calculate(1, 2);
// ❌ 不好的做法
expect(result).toBe(3);
// ✅ 好的做法 - 提供上下文
expect(result).withContext('calculate(1, 2) should return 3').toBe(3);
// ✅ 或使用自定义消息
if (result !== 3) {
throw new Error(`Expected 3, got ${result}`);
}
});
使用 try-catch #
javascript
test('debug with try-catch', () => {
try {
const result = riskyOperation();
expect(result).toBe('expected');
} catch (error) {
console.log('Error caught:', error);
console.log('Stack trace:', error.stack);
throw error; // 重新抛出以使测试失败
}
});
详细断言失败 #
javascript
test('detailed assertion', () => {
const user = { name: 'John', age: 30 };
// 使用 toEqual 而不是 toBe
expect(user).toEqual({
name: 'John',
age: 30,
});
// 使用 toMatchObject 部分匹配
expect(user).toMatchObject({
name: 'John',
});
});
调试异步代码 #
异步错误追踪 #
javascript
test('debug async', async () => {
try {
const result = await fetchData();
console.log('Result:', result);
expect(result).toBeDefined();
} catch (error) {
console.log('Async error:', error);
throw error;
}
});
Promise 拒绝调试 #
javascript
test('debug promise rejection', async () => {
await expect(fetchData()).rejects.toThrow();
// 或手动捕获
try {
await fetchData();
} catch (error) {
console.log('Rejection reason:', error);
}
});
定时器调试 #
javascript
test('debug timers', () => {
jest.useFakeTimers();
const callback = jest.fn();
setTimeout(callback, 1000);
console.log('Timer state:', jest.getTimerCount());
jest.advanceTimersByTime(500);
console.log('After 500ms:', callback.mock.calls);
jest.advanceTimersByTime(500);
console.log('After 1000ms:', callback.mock.calls);
jest.useRealTimers();
});
Mock 调试 #
查看 Mock 状态 #
javascript
test('debug mock', () => {
const mockFn = jest.fn();
mockFn('hello');
mockFn('world', 123);
// 查看 Mock 调用
console.log('Calls:', mockFn.mock.calls);
console.log('Call count:', mockFn.mock.calls.length);
console.log('First call:', mockFn.mock.calls[0]);
console.log('Last call:', mockFn.mock.calls[mockFn.mock.calls.length - 1]);
// 查看返回值
console.log('Results:', mockFn.mock.results);
});
Mock 实现调试 #
javascript
test('debug mock implementation', () => {
const mockFn = jest.fn((...args) => {
console.log('Mock called with:', args);
return args.reduce((a, b) => a + b, 0);
});
const result = mockFn(1, 2, 3);
console.log('Mock result:', result);
expect(result).toBe(6);
});
快照调试 #
查看快照差异 #
bash
# 更新快照并查看差异
jest --updateSnapshot --verbose
# 使用 git diff 查看
git diff -- "*.snap"
内联快照调试 #
javascript
test('inline snapshot debug', () => {
const data = { name: 'John' };
// 第一次运行会生成快照
expect(data).toMatchInlineSnapshot();
// 查看生成的快照
console.log('Snapshot:', expect.getState().currentTestName);
});
测试隔离调试 #
只运行一个测试 #
javascript
// 使用 only
test.only('this test only', () => {
// 测试代码
});
// 或使用命令行
jest --testNamePattern="this test only"
跳过其他测试 #
javascript
// 跳过其他测试
test.skip('skipped test', () => {});
// 或使用 describe.skip
describe.skip('skipped suite', () => {});
调试工具 #
jest-diff #
javascript
const { diff } = require('jest-diff');
test('show diff', () => {
const a = { name: 'John', age: 30 };
const b = { name: 'Jane', age: 25 };
console.log(diff(a, b));
});
pretty-format #
javascript
const prettyFormat = require('pretty-format');
test('pretty print', () => {
const user = { name: 'John', nested: { a: 1, b: 2 } };
console.log(prettyFormat(user));
});
常见问题调试 #
测试超时 #
javascript
test('debug timeout', async () => {
// 增加超时时间
}, 30000);
// 或配置全局超时
// jest.config.js
module.exports = {
testTimeout: 30000,
};
内存泄漏 #
bash
# 检测内存泄漏
jest --detectOpenHandles --forceExit
# 查看内存使用
jest --logHeapUsage
测试不执行 #
javascript
// 检查测试匹配
console.log('Test file:', __filename);
// 检查测试名称
test('my test', () => {
console.log('Test executed');
});
调试最佳实践 #
1. 使用描述性消息 #
javascript
test('should calculate total correctly', () => {
const cart = new Cart();
cart.addItem({ price: 100 });
expect(cart.total).toBe(100);
});
2. 分步调试 #
javascript
test('step by step', () => {
// 步骤 1
const user = createUser('John');
console.log('Step 1 - User created:', user);
// 步骤 2
user.setEmail('john@example.com');
console.log('Step 2 - Email set:', user.email);
// 步骤 3
const saved = saveUser(user);
console.log('Step 3 - User saved:', saved);
expect(saved.id).toBeDefined();
});
3. 使用调试器语句 #
javascript
test('debugger statement', () => {
const data = { name: 'John' };
debugger; // 在此处暂停
expect(data.name).toBe('John');
});
下一步 #
现在你已经掌握了 Jest 调试技巧,接下来学习 扩展与插件 扩展 Jest 功能!
最后更新:2026-03-28