Тестирование
Unit-тесты, интеграционные тесты и TDD, mocks vs fakes vs stubs, метрики покрытия кода, фреймворки GTest и Catch2, тестирование приватных методов.
5 вопросов
JuniorТеорияОчень частоЗачем пишутся unit-тесты? Unit vs integration vs TDD.
Зачем пишутся unit-тесты? Unit vs integration vs TDD.
Unit-тесты проверяют одну функцию/класс в изоляции с моками; быстры и детерминированы. Integration-тесты используют реальные компоненты. TDD сначала пишет падающий тест, затем минимальный код.
Типичные ошибки
- ✗Тестировать детали реализации вместо поведения — тесты, проверяющие вызовы приватных методов или внутреннее состояние, ломаются при любом рефакторинге; тестируйте публичный контракт
- ✗Писать тесты после кода с 100% покрытием как целью — покрытие измеряет, какие строки выполнились, а не какое поведение проверено; цель — осмысленные утверждения
- ✗Мокировать всё в интеграционных тестах — тесты с слишком многими моками проверяют взаимодействие с моками, а не систему; позвольте интеграционным тестам использовать реальные компоненты
Уточняющие вопросы
- →Что такое property-based тестирование и как оно дополняет тесты на примерах?
- →Как тестировать код, зависящий от текущего времени или случайных чисел?
MiddleТеорияЧастоКак тестировать C++ код? Какие фреймворки существуют (GTest, Catch2)?
Как тестировать C++ код? Какие фреймворки существуют (GTest, Catch2)?
Популярные фреймворки тестов C++: Google Test (макросы TEST, gmock), Catch2 (header-only, BDD), Boost.Test и doctest (single-header). Все интегрируются с CMake через ctest.
Типичные ошибки
- ✗Использовать
ASSERT_*в настройке фикстуры —ASSERT_*вызываетreturnпри неудаче, что не работает внутри не-void функций типаSetUp(); используйтеASSERT_*только в телахTESTилиASSERT_NO_FATAL_FAILURE - ✗Делать тесты зависящими от порядка — тесты, разделяющие глобальное состояние без очистки, могут проходить изолированно, но падать при запуске вместе; сбрасывайте состояние в
TearDown - ✗Пропускать тесты для 'временного кода' — непротестированный код становится постоянным; пишите хотя бы один smoke-тест даже для прототипов
Уточняющие вопросы
- →Как работают
EXPECT_CALLиWillOnce/WillRepeatedlyв Google Mock? - →Как измерить время выполнения тестов и выявить медленные тесты в gtest?
MiddleТеорияЧастоЧто такое mock? Когда использовать mock vs fake vs stub?
Что такое mock? Когда использовать mock vs fake vs stub?
Тестовые дублёры заменяют зависимости. Stub возвращает заготовленные значения; mock проверяет вызовы (EXPECT_CALL); fake — облегчённая рабочая реализация; spy логирует вызовы; dummy заполняет место.
Типичные ошибки
- ✗Мокировать всё, включая объекты-значения — мокирование
std::stringили простых DTO добавляет шум; мокируйте только то, что имеет побочные эффекты или I/O - ✗Писать тесты, проверяющие только взаимодействие с моками без утверждений о наблюдаемом выводе — тест становится зеркалом реализации, а не спецификацией
- ✗Использовать моки для косвенного тестирования приватных методов — если нужен мок для приватного коллаборатора, класс, вероятно, делает слишком много; разбейте его
Уточняющие вопросы
- →Чем
ON_CALLотличается отEXPECT_CALLв Google Mock? - →Что такое шов (seam) и как внедрять тестовые дублёры без изменения production-кода?
SeniorТеорияИногдаЧто такое покрытие кода и как оно измеряется?
Что такое покрытие кода и как оно измеряется?
Покрытие измеряет долю выполненного во время тестов кода: строки, ветви, функции или MC/DC. Инструменты: gcov/lcov, llvm-cov. 100% не гарантирует осмысленных утверждений.
Типичные ошибки
- ✗Оптимизировать процент покрытия вместо качества тестов — написание тестов, выполняющих код без утверждений, раздувает покрытие без выявления багов
- ✗Считать 80% покрытия всегда достаточным — для safety-critical или security-sensitive кода может потребоваться 100% покрытие ветвей (MC/DC)
- ✗Не исключать сгенерированный или сторонний код из отчётов покрытия — раздутые непокрытые строки из файлов, сгенерированных protobuf, искажают картину
Уточняющие вопросы
- →Что такое мутационное тестирование и как оно выявляет неадекватные утверждения в тестах?
- →Как интегрировать отчётность о покрытии кода в CI-конвейер с проверкой минимального порога?
SeniorТеорияИногдаКак тестировать приватные методы?
Как тестировать приватные методы?
Предпочтительно тестировать приватные методы через публичный интерфейс; сложная приватная логика говорит о выделении класса. Если нельзя — используйте friend class MyTest; или FRIEND_TEST.
Типичные ошибки
- ✗Использовать
#define private public— изменяет раскладку класса для TU, включающих заголовок, и может вызывать нарушения ODR; избегайте кроме одноразовых тестов - ✗Добавлять
friend-объявления в production-заголовки для каждого теста — засоряет интерфейс; используйтеFRIEND_TEST, который ограничивает дружбу именованным тестом - ✗Не переосмысливать дизайн — необходимость тестирования приватных методов — запах дизайна; рассмотрите, не должен ли приватный код быть отдельным, независимо тестируемым компонентом
Уточняющие вопросы
- →Как
FRIEND_TESTиз gtest работает на уровне препроцессора? - →Что такое проблема 'повреждения тестом' (test-induced damage) и как она применяется к тестированию приватных методов?