What is the significance of test coverage and methods to improve it?
考察点:测试质量评估。
答案:
测试覆盖率是衡量代码被测试程度的指标,表示测试用例执行时覆盖的代码比例。虽然高覆盖率不等于高质量测试,但它是评估测试完整性的重要参考指标。
覆盖率类型:
-
行覆盖率(Line Coverage):
function calculateDiscount(price, userType) {
if (price > 100) {
if (userType === 'vip') {
return price * 0.8;
}
return price * 0.9;
}
return price;
}
test('regular user discount', () => {
expect(calculateDiscount(150, 'regular')).toBe(135);
});
test('vip user discount', () => {
expect(calculateDiscount(150, 'vip')).toBe(120);
});
-
分支覆盖率(Branch Coverage):
describe('calculateDiscount', () => {
test('price <= 100', () => {
expect(calculateDiscount(50, 'regular')).toBe(50);
});
test('price > 100 and userType !== vip', () => {
expect(calculateDiscount(150, 'regular')).toBe(135);
});
test('price > 100 and userType === vip', () => {
expect(calculateDiscount(150, 'vip')).toBe(120);
});
});
-
函数覆盖率(Function Coverage):
export const utils = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b
};
describe('utils', () => {
test('add function', () => {
expect(utils.add(2, 3)).toBe(5);
});
});
覆盖率工具配置:
-
Jest配置:
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/index.js',
'!src/serviceWorker.js',
'!src/**/*.test.{js,jsx,ts,tsx}'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
},
'./src/utils/': {
branches: 90,
functions: 90,
lines: 90,
statements: 90
}
},
coverageReporters: ['text', 'html', 'lcov']
};
-
NYC (Istanbul) 配置:
{
"nyc": {
"include": ["src/**/*.js"],
"exclude": ["**/*.test.js", "**/*.spec.js"],
"reporter": ["text", "html", "json"],
"check-coverage": true,
"lines": 80,
"functions": 80,
"branches": 80,
"statements": 80
}
}
覆盖率提升方法:
-
识别未覆盖代码:
npm test -- --coverage --watchAll=false
open coverage/lcov-report/index.html
-
针对性补充测试:
function parseJSON(str) {
try {
return JSON.parse(str);
} catch (error) {
console.error('Invalid JSON:', error);
return null;
}
}
test('should handle invalid JSON', () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const result = parseJSON('invalid json');
expect(result).toBeNull();
expect(consoleSpy).toHaveBeenCalledWith('Invalid JSON:', expect.any(Error));
consoleSpy.mockRestore();
});
-
边界条件测试:
describe('array utils', () => {
test('empty array', () => {
expect(getAverage([])).toBeNaN();
});
test('single element', () => {
expect(getAverage([5])).toBe(5);
});
test('negative numbers', () => {
expect(getAverage([-1, -2, -3])).toBe(-2);
});
});
覆盖率最佳实践:
- 设定合理目标:一般80-90%为宜,不必追求100%
- 关注质量而非数量:重要业务逻辑应达到更高覆盖率
- 排除特定文件:配置文件、常量定义等可以排除
- 持续监控:集成到CI/CD流程中
CI/CD集成示例:
- name: Run tests with coverage
run: npm test -- --coverage --watchAll=false
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: ./coverage/lcov.info
- name: Check coverage threshold
run: |
if [ $(jq '.total.lines.pct' coverage/coverage-summary.json | cut -d. -f1) -lt 80 ]; then
echo "Coverage below threshold"
exit 1
fi
实际应用:
- 项目质量监控和代码评审依据
- 重构时确保测试覆盖完整性
- 新功能开发的测试要求制定
- 技术债务识别和改进计划制定