Jest 调试技巧 #
调试概述 #
测试调试是开发过程中的重要环节,掌握有效的调试技巧可以快速定位和解决问题。
text
┌─────────────────────────────────────────────────────────────┐
│ 调试方法 │
├─────────────────────────────────────────────────────────────┤
│ 1. 日志输出 - console.log、debugger │
│ 2. 断点调试 - VS Code、Chrome DevTools │
│ 3. 详细输出 --verbose、--detectOpenHandles │
│ 4. 错误分析 - 堆栈跟踪、错误消息 │
└─────────────────────────────────────────────────────────────┘
日志输出 #
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.table(user);
expect(user.name).toBe('John');
});
console 输出控制 #
bash
# 显示所有 console 输出
jest --verbose
# 隐藏 console 输出
jest --silent
使用 console.group #
javascript
test('grouped logs', () => {
console.group('Test Details');
console.log('Step 1: Setup');
console.log('Step 2: Execute');
console.log('Step 3: Verify');
console.groupEnd();
});
使用 util.inspect #
javascript
const util = require('util');
test('deep inspect', () => {
const obj = { a: { b: { c: { d: 1 } } } };
console.log(util.inspect(obj, { depth: null, colors: true }));
});
断点调试 #
debugger 语句 #
javascript
test('debug with debugger', () => {
const value = calculate(1, 2);
debugger; // 执行会在这里暂停
expect(value).toBe(3);
});
bash
# 运行时启用调试
node --inspect-brk node_modules/.bin/jest --runInBand
VS Code 调试 #
创建 .vscode/launch.json:
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"
},
{
"type": "node",
"request": "launch",
"name": "Jest Debug Current File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"${fileBasenameNoExtension}",
"--runInBand",
"--no-cache"
],
"console": "integratedTerminal"
}
]
}
Chrome DevTools 调试 #
bash
# 启动调试模式
node --inspect-brk node_modules/.bin/jest --runInBand
# 打开 Chrome
# chrome://inspect
# 点击 "Open dedicated DevTools for Node"
详细输出 #
–verbose #
bash
# 显示详细输出
jest --verbose
输出示例:
text
PASS src/utils/math.test.js
Calculator
add
✓ should add two numbers (2 ms)
✓ should add negative numbers (1 ms)
subtract
✓ should subtract two numbers
✓ should handle zero
–detectOpenHandles #
bash
# 检测未关闭的句柄
jest --detectOpenHandles
–forceExit #
bash
# 强制退出
jest --forceExit
–detectLeaks #
bash
# 检测内存泄漏
jest --detectLeaks
错误分析 #
理解错误消息 #
javascript
test('error example', () => {
const result = { a: 1, b: 2 };
expect(result).toEqual({ a: 1, b: 3 });
});
错误输出:
text
expect(received).toEqual(expected)
Expected: {"a": 1, "b": 3}
Received: {"a": 1, "b": 2}
Difference:
- Expected
+ Received
Object {
"a": 1,
- "b": 3,
+ "b": 2,
}
使用 .toThrow #
javascript
test('error matching', () => {
expect(() => throwError()).toThrow('Error message');
expect(() => throwError()).toThrow(Error);
expect(() => throwError()).toThrow(/error/i);
});
捕获异步错误 #
javascript
test('async error', async () => {
await expect(asyncError()).rejects.toThrow('Async error');
});
// 或使用 try/catch
test('async error with try/catch', async () => {
try {
await asyncError();
} catch (error) {
expect(error.message).toBe('Async error');
}
});
常见问题 #
测试超时 #
javascript
// 问题:测试超时
test('timeout test', async () => {
await slowOperation();
}, 5000); // 增加超时时间
// 或在配置中设置
// jest.config.js
module.exports = {
testTimeout: 10000,
};
Mock 不生效 #
javascript
// 问题:Mock 在导入之后
import { fetchData } from './api';
jest.mock('./api'); // 太晚了
// 解决方案:Mock 必须在导入之前
jest.mock('./api');
import { fetchData } from './api';
异步测试提前结束 #
javascript
// 问题:测试在异步完成前结束
test('bad async', () => {
fetchData().then(data => {
expect(data).toBe('expected');
});
// 没有 return 或 await
});
// 解决方案
test('good async', async () => {
const data = await fetchData();
expect(data).toBe('expected');
});
定时器问题 #
javascript
// 问题:定时器不执行
test('timer issue', () => {
setTimeout(() => {
expect(true).toBe(true);
}, 1000);
// 测试立即结束
});
// 解决方案
test('timer solution', () => {
jest.useFakeTimers();
const callback = jest.fn();
setTimeout(callback, 1000);
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalled();
jest.useRealTimers();
});
DOM 元素找不到 #
javascript
// 问题:元素找不到
test('element not found', () => {
render(<Component />);
screen.getByText('Text'); // 抛出错误
});
// 解决方案:使用 debug
test('debug DOM', () => {
render(<Component />);
screen.debug(); // 打印 DOM 结构
screen.getByText('Text');
});
快照不匹配 #
javascript
// 问题:快照不匹配
// 1. 检查变更是否正确
// 2. 如果正确,更新快照
// jest -u
调试工具 #
jest-dom debug #
javascript
import { screen } from '@testing-library/react';
test('debug DOM', () => {
render(<Component />);
// 打印整个 DOM
screen.debug();
// 打印特定元素
screen.debug(screen.getByRole('button'));
// 打印多个元素
screen.debug(screen.getAllByRole('listitem'));
});
prettyDOM #
javascript
import { prettyDOM } from '@testing-library/react';
test('pretty DOM', () => {
const { container } = render(<Component />);
console.log(prettyDOM(container));
console.log(prettyDOM(container.firstChild));
});
logTestingPlaygroundURL #
javascript
import { logTestingPlaygroundURL } from '@testing-library/react';
test('playground URL', () => {
render(<Component />);
// 生成 Testing Playground URL
logTestingPlaygroundURL();
});
调试模式 #
单独运行测试 #
bash
# 运行单个文件
jest sum.test.js
# 运行匹配模式的测试
jest --testNamePattern="add"
# 使用 only
test.only('only this test runs', () => {});
串行运行 #
bash
# 串行运行,更容易调试
jest --runInBand
禁用并行 #
javascript
// jest.config.js
module.exports = {
maxWorkers: 1,
};
错误处理 #
全局错误处理 #
javascript
// jest.setup.js
process.on('unhandledRejection', (reason, promise) => {
console.log('Unhandled Rejection:', reason);
});
process.on('uncaughtException', (error) => {
console.log('Uncaught Exception:', error);
});
测试错误处理 #
javascript
describe('error handling', () => {
let consoleError;
beforeEach(() => {
consoleError = console.error;
console.error = jest.fn();
});
afterEach(() => {
console.error = consoleError;
});
test('handles error', () => {
// 测试代码
expect(console.error).toHaveBeenCalled();
});
});
调试技巧清单 #
通用调试流程 #
text
1. 理解错误消息
├── 阅读错误类型
├── 查看期望值和实际值
└── 检查堆栈跟踪
2. 隔离问题
├── 只运行失败的测试
├── 使用 test.only
└── 移除无关代码
3. 添加日志
├── console.log 关键变量
├── screen.debug() DOM 结构
└── 使用 debugger 断点
4. 检查常见问题
├── Mock 是否正确
├── 异步是否处理
├── 定时器是否 Mock
└── 状态是否隔离
5. 解决问题
├── 修复代码或测试
├── 更新快照(如果需要)
└── 验证修复
调试命令速查 #
bash
# 详细输出
jest --verbose
# 串行运行
jest --runInBand
# 检测未关闭句柄
jest --detectOpenHandles
# 检测内存泄漏
jest --detectLeaks
# 强制退出
jest --forceExit
# 清除缓存
jest --clearCache
# 显示配置
jest --showConfig
# 调试模式
node --inspect-brk node_modules/.bin/jest --runInBand
最佳实践 #
1. 先理解错误再修复 #
javascript
// ❌ 不好的做法 - 盲目修改
// 随意更改代码
// ✅ 好的做法 - 理解后修复
// 1. 阅读错误消息
// 2. 理解期望值和实际值
// 3. 找出差异原因
// 4. 针对性修复
2. 使用最小化测试 #
javascript
// ❌ 不好的做法 - 在复杂测试中调试
test('complex test', () => {
// 很多代码...
// 错误发生在这里
});
// ✅ 好的做法 - 创建最小化测试
test('minimal test', () => {
const result = problematicFunction();
expect(result).toBe('expected');
});
3. 保持测试独立 #
javascript
// ❌ 不好的做法 - 测试依赖
let sharedState;
test('test 1', () => {
sharedState = 'value';
});
test('test 2', () => {
// 依赖 test 1
expect(sharedState).toBe('value');
});
// ✅ 好的做法 - 测试独立
test('test 1', () => {
const state = setup();
expect(state).toBeDefined();
});
test('test 2', () => {
const state = setup();
expect(state).toBeDefined();
});
下一步 #
现在你已经掌握了 Jest 调试技巧,接下来学习 React 测试 实战 React 组件测试!
最后更新:2026-03-28