Тестирование в C++: обзор фреймворков и инструментов
Тестирование — неотъемлемая часть профессиональной разработки на C++. В отличие от языков с динамической типизацией, C++ требует особого внимания к управлению памятью, RAII и шаблонным метапрограммам. Без качественных тестов даже небольшой рефакторинг может привести к трудноуловимым ошибкам. В этой статье мы разберём основные фреймворки для модульного тестирования в C++, их особенности и примеры использования.
Зачем тестировать C++ код?
Основные причины внедрения тестирования:
- Безопасность рефакторинга — тесты фиксируют корректное поведение до изменений.
- Документация — тесты показывают, как использовать API.
- Обнаружение регрессий — автоматическая проверка после каждого коммита.
- Валидация граничных случаев — работа с nullptr, пустыми контейнерами, переполнением.
Популярные фреймворки для C++
1. Google Test (gtest)
Самый распространённый фреймворк от Google. Поддерживает автоматическую регистрацию тестов, макросы ASSERT_* и EXPECT_*, параметризованные тесты, моки через Google Mock.
#include <gtest/gtest.h>
int add(int a, int b) { return a + b; }
TEST(AdditionTest, PositiveNumbers) {
EXPECT_EQ(add(2, 3), 5);
EXPECT_EQ(add(0, 0), 0);
}
TEST(AdditionTest, NegativeNumbers) {
EXPECT_EQ(add(-1, -2), -3);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Особенности:
- Поддержка смертельных тестов (death tests) для проверки assert.
- Фабрика моков (gmock) для изоляции зависимостей.
- Интеграция с CMake (find_package(GTest)).
2. Catch2
Современный фреймворк с выразительным синтаксисом. Не требует отдельной регистрации тестов — достаточно макроса TEST_CASE. Поддерживает BDD-стиль (Given-When-Then).
#include <catch2/catch_test_macros.hpp>
int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
TEST_CASE("Factorials are computed", "[math]") {
REQUIRE(factorial(0) == 1);
REQUIRE(factorial(1) == 1);
REQUIRE(factorial(5) == 120);
}
TEST_CASE("Negative input", "[math][edge]") {
REQUIRE_THROWS_AS(factorial(-1), std::domain_error);
}
Особенности:
- Генерация тестовых данных (GENERATE).
- Разделение тестов на секции (SECTION).
- Автоматический выбор main() (если определить CATCH_CONFIG_MAIN).
3. doctest
Лёгкий и быстрый фреймворк, минимальное время компиляции. Идеален для проектов, где важна скорость сборки. Синтаксис похож на Catch2.
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
int divide(int a, int b) {
if (b == 0) throw std::invalid_argument("division by zero");
return a / b;
}
TEST_CASE("Division") {
CHECK(divide(10, 2) == 5);
CHECK_THROWS_AS(divide(5, 0), std::invalid_argument);
}
Особенности:
- Очень быстрая компиляция (заголовочный файл ~5 КБ).
- Поддержка макросов CHECK, REQUIRE, WARN.
- Встроенный репортёр в XML/JSON.
4. Boost.Test
Часть библиотек Boost. Имеет богатые возможности, но требует больше boilerplate-кода. Поддерживает автоматическую и ручную регистрацию тестов.
#define BOOST_TEST_MODULE MyTest
#include <boost/test/included/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(MathTests)
BOOST_AUTO_TEST_CASE(TestSum) {
BOOST_CHECK_EQUAL(2 + 2, 4);
BOOST_CHECK_CLOSE(3.14159, 3.14, 0.1); // точность 0.1%
}
BOOST_AUTO_TEST_CASE(TestFail) {
BOOST_REQUIRE_MESSAGE(false, "This test always fails");
}
BOOST_AUTO_TEST_SUITE_END()
Особенности:
- Широкий набор