Бытует мнение, что 100% покрытие кода юнит-тестами обеспечивает безопасность и уверенность в его корректности. Это заблуждение: процент покрытия кода никак не связан с качеством набора тестов.
Рассмотрим простую функцию и её юнит-тест:
function isEmptyString(string) {
if (string.length === 0) {
return true;
}
return false;
}
it('works', () => {
expect(isEmptyString('test')).toBe(false);
});
Такой тест покрывает 75% строк и 50% путей выполнения:
PASS ./index.spec.js
✓ works (4ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 75 | 50 | 100 | 75 | |
index.js | 75 | 50 | 100 | 75 | 3 |
----------|----------|----------|----------|----------|-------------------|
Довольно посредственный результат. Давайте его улучшим:
function isEmptyString(string) {
return string.length === 0;
}
Ух! Везде соточки:
PASS ./index.spec.js
✓ works (5ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Ловкость рук и никакого мошенничества. Мы смогли довести покрытие до 100%, просто поменяв сам тестируемый код, но не тесты! Метрики покрытия улучшились, а вот качество набора тестов осталось таким же.
Можно пойти дальше и, как полагается, порефакторить тесты:
it('works', () => {
isEmptyString('test');
});
Результат снова потрясающий:
PASS ./index.spec.js
✓ works (2ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Мы выкинули единственную проверку из теста, сделав его, по сути, ещё более бесполезным, но покрытие так и осталось 100%.
Мораль
Покрытие кода работает хорошо как индикатор проблем: если оно низкое, вероятно, тестов написано слишком мало. Но использовать покрытие как индикатор качества тестов нельзя: высокого покрытия можно добиться даже без единой проверки.