1. Инструменты для измерения покрытия
1.1. GCOV + LCOV
Как использовать:
# Компиляция с флагами покрытия
g++ -fprofile-arcs -ftest-coverage -O0 -g myapp.cpp -o myapp
# Запуск тестов
./myapp_tests
# Генерация отчетов
gcov myapp.cpp
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory coverage_report
Плюсы:
- Интеграция с GCC/Clang
- Подробные отчеты по строкам и ветвям
- Поддержка HTML-отчетов через LCOV
1.2. LLVM Coverage
Как использовать:
clang++ -fprofile-instr-generate -fcoverage-mapping test.cpp -o test
./test
llvm-profdata merge -sparse default.profraw -o default.profdata
llvm-cov show ./test -instr-profile=default.profdata
Преимущества:
- Более точные данные, чем gcov
- Поддержка санитайзеров
- Интеграция с LLVM инструментами
1.3. Visual Studio Coverage
Включение:
- В свойствах проекта:
Configuration Properties > Code Coverage > Enable Code Coverage
- Запуск тестов через Test Explorer
- Анализ через окно "Code Coverage Results"
2. Метрики покрытия
2.1. Покрытие строк
- Процент исполненных строк кода
2.2. Покрытие ветвей
- Процент пройденных условных переходов
2.3. Покрытие функций
- Процент вызванных функций
2.4. Покрытие условий
- Все комбинации условий в логических выражениях
3. Нужно ли измерять покрытие?
Аргументы ЗА:
- Объективная метрика - цифры вместо субъективных оценок
- Поиск "мертвого" кода - выявление неисполняемых участков
- Фокус тестирования - выявление слабо покрытых модулей
- CI/CD интеграция - контроль качества перед мерджем
Аргументы ПРОТИВ:
- Ложное чувство безопасности - 100% покрытие ≠ отсутствие багов
- Оверхед - увеличение времени сборки
- Искажения - тесты могут гнаться за покрытием, а не качеством
4. Практические рекомендации
4.1. Что считать хорошим покрытием?
- 70-80% - приемлемый уровень для legacy-кода
- 90-95% - цель для новых проектов
- 100% - только для критически важных компонентов
4.2. Как использовать метрики правильно
// Плохо: тест ради покрытия
TEST(MyTest, UselessTest) {
MyClass obj;
obj.method(); // Проверок нет, но покрытие растет
}
// Хорошо: осмысленный тест
TEST(MyTest, MeaningfulTest) {
MyClass obj;
auto result = obj.method();
ASSERT_EQ(expected, result); // И покрытие, и проверка логики
}
4.3. Интеграция в CI/CD
Пример для GitLab CI:
test_with_coverage:
script:
- g++ --coverage -O0 -g tests.cpp -o tests
- ./tests
- gcovr --exclude-unreachable-branches --xml-pretty -o coverage.xml
- gcovr --exclude-unreachable-branches --print-summary
artifacts:
reports:
cobertura: coverage.xml
5. Альтернативные подходы
5.1. Мутационное тестирование
Инструменты вроде Mull проверяют, могут ли тесты обнаружить искусственно внесенные ошибки
5.2. Статический анализ
Clang-Tidy, Cppcheck могут находить непроверенные edge cases
5.3. Property-based тестирование
Библиотеки вроде RapidCheck проверяют инварианты для случайных входных данных
Резюмируем
Измерение покрытия тестами:
-
Как делать?
- Инструменты: GCOV/LCOV, LLVM Coverage, VS Coverage
- Метрики: строки, ветви, функции
- Интеграция в CI/CD
-
Нужно ли?
- Да, как объективный показатель, но не как самоцель
- В сочетании с другими метриками качества кода
- С учетом контекста проекта (legacy vs new code)
-
Как интерпретировать?
- Низкое покрытие - явный сигнал к действию
- Высокое покрытие - не гарантия качества
- Важнее тест-кейсы, чем процент покрытия
Золотое правило: "Покрытие тестами показывает, что код работает, а не что он работает правильно". Используйте метрики покрытия как один из многих инструментов контроля качества.