测试套件中让 bug 逃逸的漏洞
理想路径已测试,但空值、空数组和边界条件呢?
时而通过时而失败的测试——CI/CD 信心的隐形杀手
相互依赖或共享可变状态的测试——调试噩梦
从单元测试到 E2E,测试分析师全面审查
函数和类级别的测试
组件交互测试
端到端用户流程测试
端点和契约测试
UI 回归检测
负载和基准测试
测试改进的真实案例
describe('calculateDiscount', () => {
it('applies 10% for orders over $100', () => {
expect(calculateDiscount(150)).toBe(15)
})
it('applies 5% for orders over $50', () => {
expect(calculateDiscount(75)).toBe(3.75)
})
// What about $0? Negative? Exactly $50? $100?
})缺少边界值测试:$0、$50、$100、负数
describe('calculateDiscount', () => {
it('applies 10% for orders over $100', () => {
expect(calculateDiscount(150)).toBe(15)
})
it('applies 5% for orders over $50', () => {
expect(calculateDiscount(75)).toBe(3.75)
})
it('returns 0 for orders at boundary', () => {
expect(calculateDiscount(50)).toBe(0)
expect(calculateDiscount(100)).toBe(5) // 5% tier
})
it('handles zero and negative gracefully', () => {
expect(calculateDiscount(0)).toBe(0)
expect(calculateDiscount(-10)).toBe(0)
})
})为边界值和无效输入添加边缘情况测试
it('shows notification after save', async () => {
await user.click(saveButton)
// Flaky! Depends on timing
await waitFor(() => {
expect(screen.getByText('Saved!')).toBeVisible()
})
// Even worse: arbitrary timeout
await new Promise(r => setTimeout(r, 100))
expect(notificationCount).toBe(1)
})任意超时和时间假设
it('shows notification after save', async () => {
await user.click(saveButton)
// Wait for specific state change
await waitFor(() => {
expect(screen.getByRole('alert')).toHaveTextContent('Saved!')
})
// Assert on observable behavior, not timing
expect(
await screen.findByRole('alert', { name: /saved/i })
).toBeVisible()
})等待可观察的状态变化,而非时间
let testUser: User
beforeAll(async () => {
// Shared across ALL tests — mutations leak!
testUser = await createUser({ name: 'Test' })
})
it('updates user name', async () => {
await updateUser(testUser.id, { name: 'Updated' })
// Now testUser.name is 'Updated' for all following tests
})
it('checks original name', () => {
// FAILS! Previous test mutated shared state
expect(testUser.name).toBe('Test')
})beforeAll 中的共享状态在测试之间泄漏
describe('user updates', () => {
let testUser: User
beforeEach(async () => {
// Fresh user for EACH test
testUser = await createUser({ name: 'Test' })
})
afterEach(async () => {
await deleteUser(testUser.id)
})
it('updates user name', async () => {
await updateUser(testUser.id, { name: 'Updated' })
expect(testUser.name).toBe('Updated')
})
it('checks original name', () => {
// Works! Fresh testUser with original name
expect(testUser.name).toBe('Test')
})
})使用 beforeEach 实现测试隔离
测试分析师不只是计算测试数量——它分析测试实际测试了什么。 它找出您的测试覆盖与代码需求之间的差距。
映射代码路径以找出未测试的分支
识别时间和顺序依赖
推荐需要添加的具体测试用例
分析测试覆盖率
映射哪些代码路径已测试,哪些未测试
识别薄弱点
找到没有测试的边缘情况和错误条件
检测反模式
发现不稳定模式和隔离问题
建议改进
推荐需要添加的具体测试用例
糟糕的测试比没有测试更糟——它们给人虚假的信心
覆盖边缘情况的测试能在用户之前发现 bug
不再需要"重试直到变绿"——测试通过或失败都有真实原因
好的测试让您可以放心地修改代码
100% 覆盖率毫无意义,如果测试不能捕获 bug。
测试分析师确保您的测试真正有效。