Как считать покрытие тестами? Нужно ли это делать?cplus-12

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

Включение:

  1. В свойствах проекта: Configuration Properties > Code Coverage > Enable Code Coverage
  2. Запуск тестов через Test Explorer
  3. Анализ через окно "Code Coverage Results"

2. Метрики покрытия

2.1. Покрытие строк

  • Процент исполненных строк кода

2.2. Покрытие ветвей

  • Процент пройденных условных переходов

2.3. Покрытие функций

  • Процент вызванных функций

2.4. Покрытие условий

  • Все комбинации условий в логических выражениях

3. Нужно ли измерять покрытие?

Аргументы ЗА:

  1. Объективная метрика - цифры вместо субъективных оценок
  2. Поиск "мертвого" кода - выявление неисполняемых участков
  3. Фокус тестирования - выявление слабо покрытых модулей
  4. CI/CD интеграция - контроль качества перед мерджем

Аргументы ПРОТИВ:

  1. Ложное чувство безопасности - 100% покрытие ≠ отсутствие багов
  2. Оверхед - увеличение времени сборки
  3. Искажения - тесты могут гнаться за покрытием, а не качеством

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 проверяют инварианты для случайных входных данных

Резюмируем

Измерение покрытия тестами:

  1. Как делать?

    • Инструменты: GCOV/LCOV, LLVM Coverage, VS Coverage
    • Метрики: строки, ветви, функции
    • Интеграция в CI/CD
  2. Нужно ли?

    • Да, как объективный показатель, но не как самоцель
    • В сочетании с другими метриками качества кода
    • С учетом контекста проекта (legacy vs new code)
  3. Как интерпретировать?

    • Низкое покрытие - явный сигнал к действию
    • Высокое покрытие - не гарантия качества
    • Важнее тест-кейсы, чем процент покрытия

Золотое правило: "Покрытие тестами показывает, что код работает, а не что он работает правильно". Используйте метрики покрытия как один из многих инструментов контроля качества.