Jest 基础测试 #
测试的基本结构 #
test 函数 #
test 是 Jest 中最基础的测试函数,用于定义一个测试用例:
javascript
test('测试描述', () => {
// 测试代码
});
第一个测试 #
javascript
// sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
// sum.test.js
const sum = require('./sum');
test('1 + 2 应该等于 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('0 + 0 应该等于 0', () => {
expect(sum(0, 0)).toBe(0);
});
test('负数相加', () => {
expect(sum(-1, -2)).toBe(-3);
});
it 函数 #
it 是 test 的别名,功能完全相同:
javascript
it('should add two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
// test 和 it 可以混用
test('test 函数', () => {
expect(true).toBe(true);
});
it('it 函数', () => {
expect(true).toBe(true);
});
组织测试 #
describe 块 #
使用 describe 将相关测试分组:
javascript
describe('Calculator', () => {
describe('add', () => {
test('adds positive numbers', () => {
expect(add(1, 2)).toBe(3);
});
test('adds negative numbers', () => {
expect(add(-1, -2)).toBe(-3);
});
});
describe('subtract', () => {
test('subtracts positive numbers', () => {
expect(subtract(5, 3)).toBe(2);
});
test('subtracts negative numbers', () => {
expect(subtract(-5, -3)).toBe(-2);
});
});
});
嵌套 describe #
javascript
describe('Math', () => {
describe('Calculator', () => {
describe('add', () => {
test('adds two numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
describe('multiply', () => {
test('multiplies two numbers', () => {
expect(multiply(2, 3)).toBe(6);
});
});
});
});
测试输出结构 #
text
PASS math.test.js
Math
Calculator
add
✓ adds two numbers (2 ms)
multiply
✓ multiplies two numbers (1 ms)
测试命名规范 #
好的命名 #
javascript
// ✅ 描述行为
test('should return sum of two numbers', () => {});
test('should throw error when input is invalid', () => {});
test('should return empty array when no items found', () => {});
// ✅ 描述预期结果
test('returns true when user is logged in', () => {});
test('returns false when password is incorrect', () => {});
// ✅ 使用中文描述(团队约定)
test('用户登录成功后应该返回 token', () => {});
test('密码错误时应该抛出异常', () => {});
不好的命名 #
javascript
// ❌ 太模糊
test('test1', () => {});
test('works', () => {});
// ❌ 技术细节而非行为
test('calls function', () => {});
test('returns value', () => {});
// ❌ 太长
test('should return the correct sum when adding two positive numbers together', () => {});
跳过测试 #
skip 方法 #
javascript
// 跳过单个测试
test.skip('这个测试被跳过', () => {
expect(true).toBe(false);
});
// 跳过 describe 块
describe.skip('Calculator', () => {
test('adds numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
// 使用 it.skip
it.skip('skipped test', () => {
expect(true).toBe(true);
});
条件跳过 #
javascript
const shouldRun = process.env.RUN_SLOW_TESTS === 'true';
test.skipIf(!shouldRun)('slow test', () => {
// 耗时测试
});
仅运行特定测试 #
javascript
// 只运行这一个测试
test.only('只运行这个测试', () => {
expect(true).toBe(true);
});
// 只运行这个 describe 块
describe.only('Calculator', () => {
test('adds numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
// 其他测试都会被跳过
test('这个测试会被跳过', () => {
expect(true).toBe(true);
});
测试结构最佳实践 #
AAA 模式 #
Arrange(准备)- Act(执行)- Assert(断言):
javascript
test('should add item to cart', () => {
// Arrange - 准备测试数据
const cart = new Cart();
const item = { id: 1, name: 'Product', price: 100 };
// Act - 执行被测试的操作
cart.addItem(item);
// Assert - 验证结果
expect(cart.items).toHaveLength(1);
expect(cart.items[0]).toEqual(item);
expect(cart.total).toBe(100);
});
单一职责 #
每个测试只验证一个行为:
javascript
// ✅ 好的做法 - 每个测试一个断言点
test('should calculate total correctly', () => {
const cart = new Cart();
cart.addItem({ price: 100 });
cart.addItem({ price: 200 });
expect(cart.total).toBe(300);
});
test('should apply discount correctly', () => {
const cart = new Cart();
cart.addItem({ price: 100 });
cart.applyDiscount(0.1);
expect(cart.total).toBe(90);
});
// ❌ 不好的做法 - 一个测试验证多个行为
test('cart operations', () => {
const cart = new Cart();
cart.addItem({ price: 100 });
expect(cart.items).toHaveLength(1);
expect(cart.total).toBe(100);
cart.applyDiscount(0.1);
expect(cart.total).toBe(90);
});
测试隔离 #
每个测试应该独立,不依赖其他测试:
javascript
// ✅ 好的做法 - 每个测试独立
describe('User', () => {
let user;
beforeEach(() => {
user = new User('John'); // 每个测试都有新的 user
});
test('should have correct name', () => {
expect(user.name).toBe('John');
});
test('should update name', () => {
user.setName('Jane');
expect(user.name).toBe('Jane');
});
});
// ❌ 不好的做法 - 测试之间有依赖
describe('User', () => {
let user = new User('John');
test('should have correct name', () => {
expect(user.name).toBe('John');
});
test('should update name', () => {
// 依赖上一个测试的状态
user.setName('Jane');
expect(user.name).toBe('Jane');
});
});
测试文件组织 #
文件命名 #
text
src/
├── utils/
│ ├── math.js
│ ├── math.test.js # 同目录放置测试文件
│ ├── string.js
│ └── string.test.js
├── components/
│ ├── Button.jsx
│ └── Button.test.jsx
└── __tests__/ # 集中放置测试文件
├── integration/
│ └── api.test.js
└── unit/
└── utils.test.js
测试文件结构 #
javascript
// user.test.js
// 1. 导入依赖
import { User } from './user';
import { Database } from './database';
// 2. Mock(如果需要)
jest.mock('./database');
// 3. 主要测试套件
describe('User', () => {
// 4. 共享变量
let user;
let mockDb;
// 5. 生命周期钩子
beforeEach(() => {
mockDb = new Database();
user = new User(mockDb);
});
afterEach(() => {
jest.clearAllMocks();
});
// 6. 分组测试
describe('constructor', () => {
test('should create user with default values', () => {
expect(user.id).toBeDefined();
expect(user.name).toBe('');
});
});
describe('setName', () => {
test('should update name', () => {
user.setName('John');
expect(user.name).toBe('John');
});
test('should throw error for empty name', () => {
expect(() => user.setName('')).toThrow('Name cannot be empty');
});
});
describe('save', () => {
test('should save to database', async () => {
await user.save();
expect(mockDb.save).toHaveBeenCalled();
});
});
});
测试模式 #
参数化测试 #
javascript
// 使用 test.each 进行参数化测试
test.each([
[1, 2, 3],
[2, 3, 5],
[5, 5, 10],
])('add(%i, %i) should return %i', (a, b, expected) => {
expect(add(a, b)).toBe(expected);
});
// 使用对象参数
test.each([
{ a: 1, b: 2, expected: 3 },
{ a: 2, b: 3, expected: 5 },
{ a: -1, b: -2, expected: -3 },
])('add($a, $b) should return $expected', ({ a, b, expected }) => {
expect(add(a, b)).toBe(expected);
});
// describe.each
describe.each([
['positive', 1, 2, 3],
['negative', -1, -2, -3],
])('%s numbers', (name, a, b, expected) => {
test(`add(${a}, ${b}) = ${expected}`, () => {
expect(add(a, b)).toBe(expected);
});
});
表格驱动测试 #
javascript
const testCases = [
{ input: 'hello', expected: 'HELLO' },
{ input: 'WORLD', expected: 'WORLD' },
{ input: 'MiXeD', expected: 'MIXED' },
{ input: '', expected: '' },
];
describe('toUpperCase', () => {
testCases.forEach(({ input, expected }) => {
test(`"${input}" should become "${expected}"`, () => {
expect(toUpperCase(input)).toBe(expected);
});
});
});
错误处理测试 #
测试抛出错误 #
javascript
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
}
test('should throw error when dividing by zero', () => {
expect(() => divide(1, 0)).toThrow('Division by zero');
expect(() => divide(1, 0)).toThrow(Error);
expect(() => divide(1, 0)).toThrow(/division/i);
});
// 测试异步错误
test('should throw error for invalid input', async () => {
await expect(fetchUser(-1)).rejects.toThrow('Invalid user ID');
});
测试异步代码 #
回调函数 #
javascript
// 使用 done 参数
test('callback test', (done) => {
function callback(data) {
expect(data).toBe('result');
done();
}
fetchData(callback);
});
Promise #
javascript
// 返回 Promise
test('promise test', () => {
return fetchData().then(data => {
expect(data).toBe('result');
});
});
// 使用 resolves/rejects
test('resolves test', () => {
return expect(fetchData()).resolves.toBe('result');
});
test('rejects test', () => {
return expect(fetchError()).rejects.toThrow('error');
});
async/await #
javascript
test('async test', async () => {
const data = await fetchData();
expect(data).toBe('result');
});
test('async error test', async () => {
await expect(fetchError()).rejects.toThrow('error');
});
实用技巧 #
测试描述模板 #
javascript
describe('功能名称', () => {
describe('方法名称', () => {
describe('场景描述', () => {
test('应该 [预期行为]', () => {
// 测试代码
});
});
});
});
// 示例
describe('ShoppingCart', () => {
describe('addItem', () => {
describe('当商品库存充足时', () => {
test('应该成功添加商品到购物车', () => {
// ...
});
});
describe('当商品库存不足时', () => {
test('应该抛出库存不足错误', () => {
// ...
});
});
});
});
使用注释分隔 #
javascript
describe('UserService', () => {
// ============================================
// 创建用户
// ============================================
describe('create', () => {
test('should create user with valid data', () => {});
test('should throw error for invalid email', () => {});
});
// ============================================
// 更新用户
// ============================================
describe('update', () => {
test('should update user name', () => {});
test('should throw error for non-existent user', () => {});
});
});
下一步 #
现在你已经掌握了 Jest 基础测试的编写方法,接下来学习 断言匹配器 了解更多断言方式!
最后更新:2026-03-28