test-coverage-improver
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTest Coverage Improver
测试覆盖率提升指南
Instructions
操作步骤
When improving test coverage:
- Run coverage report to identify gaps
- Prioritize critical/complex code paths
- Write tests for uncovered code
- Verify coverage improved
当需要提升测试覆盖率时:
- 生成覆盖率报告以识别缺口
- 优先处理关键/复杂代码路径
- 为未覆盖代码编写测试用例
- 验证覆盖率是否提升
Generate Coverage Report
生成覆盖率报告
bash
undefinedbash
undefinedJest
Jest
npx jest --coverage
npx jest --coverage
Vitest
Vitest
npx vitest --coverage
npx vitest --coverage
NYC (Istanbul) for any test runner
NYC (Istanbul) for any test runner
npx nyc npm test
npx nyc npm test
View HTML report
View HTML report
open coverage/lcov-report/index.html
undefinedopen coverage/lcov-report/index.html
undefinedCoverage Targets
覆盖率目标
| Type | Minimum | Good | Excellent |
|---|---|---|---|
| Lines | 70% | 80% | 90%+ |
| Branches | 60% | 75% | 85%+ |
| Functions | 70% | 80% | 90%+ |
| Statements | 70% | 80% | 90%+ |
| 类型 | 最低标准 | 良好标准 | 优秀标准 |
|---|---|---|---|
| 代码行 | 70% | 80% | 90%+ |
| 分支 | 60% | 75% | 85%+ |
| 函数 | 70% | 80% | 90%+ |
| 语句 | 70% | 80% | 90%+ |
Test Templates
测试模板
Unit Test (Jest/Vitest)
单元测试(Jest/Vitest)
typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { calculateTotal, formatCurrency } from './utils';
describe('calculateTotal', () => {
it('should sum all item prices', () => {
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 1 },
];
expect(calculateTotal(items)).toBe(25);
});
it('should return 0 for empty array', () => {
expect(calculateTotal([])).toBe(0);
});
it('should handle decimal prices', () => {
const items = [{ price: 10.99, quantity: 1 }];
expect(calculateTotal(items)).toBeCloseTo(10.99);
});
});typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { calculateTotal, formatCurrency } from './utils';
describe('calculateTotal', () => {
it('should sum all item prices', () => {
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 1 },
];
expect(calculateTotal(items)).toBe(25);
});
it('should return 0 for empty array', () => {
expect(calculateTotal([])).toBe(0);
});
it('should handle decimal prices', () => {
const items = [{ price: 10.99, quantity: 1 }];
expect(calculateTotal(items)).toBeCloseTo(10.99);
});
});Testing Async Functions
异步函数测试
typescript
describe('fetchUser', () => {
it('should return user data', async () => {
const user = await fetchUser(1);
expect(user).toEqual({
id: 1,
name: expect.any(String),
email: expect.stringContaining('@'),
});
});
it('should throw for non-existent user', async () => {
await expect(fetchUser(999)).rejects.toThrow('User not found');
});
});typescript
describe('fetchUser', () => {
it('should return user data', async () => {
const user = await fetchUser(1);
expect(user).toEqual({
id: 1,
name: expect.any(String),
email: expect.stringContaining('@'),
});
});
it('should throw for non-existent user', async () => {
await expect(fetchUser(999)).rejects.toThrow('User not found');
});
});Mocking Dependencies
依赖Mock
typescript
import { vi } from 'vitest';
import { sendEmail } from './email';
import { createUser } from './user';
vi.mock('./email', () => ({
sendEmail: vi.fn().mockResolvedValue({ success: true }),
}));
describe('createUser', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should send welcome email after creating user', async () => {
await createUser({ name: 'John', email: 'john@test.com' });
expect(sendEmail).toHaveBeenCalledWith({
to: 'john@test.com',
template: 'welcome',
});
});
});typescript
import { vi } from 'vitest';
import { sendEmail } from './email';
import { createUser } from './user';
vi.mock('./email', () => ({
sendEmail: vi.fn().mockResolvedValue({ success: true }),
}));
describe('createUser', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should send welcome email after creating user', async () => {
await createUser({ name: 'John', email: 'john@test.com' });
expect(sendEmail).toHaveBeenCalledWith({
to: 'john@test.com',
template: 'welcome',
});
});
});React Component Testing
React组件测试
tsx
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
describe('Button', () => {
it('should render children', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('should call onClick when clicked', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click</Button>);
await userEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('should be disabled when loading', () => {
render(<Button isLoading>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});tsx
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
describe('Button', () => {
it('should render children', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('should call onClick when clicked', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click</Button>);
await userEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('should be disabled when loading', () => {
render(<Button isLoading>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});API Route Testing
API路由测试
typescript
import { createMocks } from 'node-mocks-http';
import handler from './api/users';
describe('GET /api/users', () => {
it('should return users list', async () => {
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(JSON.parse(res._getData())).toHaveProperty('users');
});
});typescript
import { createMocks } from 'node-mocks-http';
import handler from './api/users';
describe('GET /api/users', () => {
it('should return users list', async () => {
const { req, res } = createMocks({ method: 'GET' });
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(JSON.parse(res._getData())).toHaveProperty('users');
});
});Branch Coverage Checklist
分支覆盖率检查清单
Ensure tests cover:
- If/else branches
- Ternary operators
- Switch cases (including default)
- Try/catch blocks
- Early returns
- Nullish coalescing ()
?? - Optional chaining results ()
?. - Loop conditions (0, 1, many iterations)
确保测试覆盖以下内容:
- If/else分支
- 三元运算符
- Switch语句(包括default分支)
- Try/catch块
- 提前返回
- 空值合并运算符()
?? - 可选链结果()
?. - 循环条件(0次、1次、多次迭代)
Coverage Configuration
覆盖率配置
javascript
// vitest.config.ts
export default {
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
exclude: [
'node_modules/',
'**/*.d.ts',
'**/*.test.ts',
'**/types/',
],
thresholds: {
lines: 80,
branches: 75,
functions: 80,
statements: 80,
},
},
},
};javascript
// vitest.config.ts
export default {
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
exclude: [
'node_modules/',
'**/*.d.ts',
'**/*.test.ts',
'**/types/',
],
thresholds: {
lines: 80,
branches: 75,
functions: 80,
statements: 80,
},
},
},
};Priority Order for Testing
测试优先级顺序
- Critical paths: Auth, payments, data mutations
- Complex logic: Algorithms, state machines, calculations
- Error handlers: Catch blocks, error boundaries
- Edge cases: Empty arrays, null values, boundaries
- Integration points: API calls, database queries
- 关键路径:认证、支付、数据变更
- 复杂逻辑:算法、状态机、计算逻辑
- 错误处理:Catch块、错误边界
- 边缘情况:空数组、空值、边界值
- 集成点:API调用、数据库查询