单元测试指南
好测试的 FIRST 原则
| 原则 | 含义 |
|---|---|
| Fast(快速) | 毫秒级执行;无 sleep、无 I/O |
| Independent(独立) | 测试间无共享状态;执行顺序无关 |
| Repeatable(可重复) | 每次结果相同;无随机性、无时间依赖 |
| Self-validating(自验证) | 自动判断通过/失败,无需人工检查 |
| Timely(及时) | 与生产代码同步编写(TDD)或立即跟进 |
测试结构:AAA 模式
// JavaScript(Vitest / Jest)——AAA 模式
import { describe, it, expect, beforeEach } from 'vitest';
import { ShoppingCart } from './cart';
describe('ShoppingCart', () => {
let cart;
beforeEach(() => {
cart = new ShoppingCart(); // 每个测试重置状态
});
it('无商品时总价为 0', () => {
// Arrange(在 beforeEach 中完成)
// Act
const total = cart.total();
// Assert
expect(total).toBe(0);
});
it('多商品含折扣时正确计算总价', () => {
// Arrange
cart.add({ id: 1, price: 100, qty: 2 });
cart.add({ id: 2, price: 50, qty: 1 });
cart.applyDiscount(10); // 9折
// Act
const total = cart.total();
// Assert
expect(total).toBe(225); // (200 + 50) * 0.9
});
it('添加负价格商品时抛出异常', () => {
expect(() => cart.add({ id: 1, price: -5, qty: 1 }))
.toThrow('Price must be non-negative');
});
});
Go 单元测试
// Go——表驱动测试(惯用写法)
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"正整数", 2, 3, 5},
{"负正混合", -2, 5, 3},
{"均为负数", -3, -4, -7},
{"零值", 0, 0, 0},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := calculator.Add(tc.a, tc.b)
if got != tc.want {
t.Errorf("Add(%d, %d) = %d, want %d", tc.a, tc.b, got, tc.want)
}
})
}
}
// 使用 testify 简化断言
func TestDivide(t *testing.T) {
result, err := calculator.Divide(10, 2)
require.NoError(t, err)
assert.Equal(t, 5.0, result)
_, err = calculator.Divide(10, 0)
assert.ErrorIs(t, err, calculator.ErrDivisionByZero)
}