Тестирование в C++: обзор фреймворков и инструментов (Google Test, Catch2, doctest)

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Тестирование в C++: фреймворки и инструменты

Качественное тестирование — неотъемлемая часть разработки на C++. В отличие от динамических языков, C++ требует особого внимания к управлению памятью, времени жизни объектов и производительности. В этой статье мы разберём три самых популярных фреймворка для модульного тестирования: Google Test, Catch2 и doctest, а также коснёмся дополнительных инструментов.

Зачем нужно тестирование в C++?

C++ — язык с ручным управлением памятью и мощной шаблонной магией. Без тестов легко допустить:

  • Утечки памяти и двойное освобождение
  • Неопределённое поведение (UB) при работе с указателями
  • Ошибки в перегрузке операторов и конструкторах копирования
  • Регрессии при рефакторинге легаси-кода

Фреймворки тестирования автоматизируют проверку и помогают быстро находить проблемы.

Google Test (gtest)

Самый распространённый фреймворк от Google. Поддерживает все основные функции: макросы ASSERT_* и EXPECT_*, параметризованные тесты, фикстуры (test fixtures), death-тесты.

Установка (через CMake FetchContent)

include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG release-1.14.0
)
FetchContent_MakeAvailable(googletest)

Пример простого теста

#include <gtest/gtest.h>

int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

TEST(FactorialTest, HandlesZeroInput) {
    EXPECT_EQ(factorial(0), 1);
}

TEST(FactorialTest, HandlesPositiveInput) {
    EXPECT_EQ(factorial(5), 120);
    EXPECT_EQ(factorial(3), 6);
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Фикстуры и параметризованные тесты

class MathFixture : public ::testing::Test {
protected:
    void SetUp() override { /* инициализация */ }
    void TearDown() override { /* очистка */ }
};

// Параметризованный тест
class SumTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};
TEST_P(SumTest, CheckSum) {
    auto [a, b, expected] = GetParam();
    EXPECT_EQ(a + b, expected);
}
INSTANTIATE_TEST_SUITE_P(SumParams, SumTest,
    ::testing::Values(
        std::make_tuple(1, 1, 2),
        std::make_tuple(0, 0, 0)
    ));

Плюсы: огромное сообщество, интеграция с CI/CD, мощные макросы, поддержка death-тестов.
Минусы: относительно медленная компиляция, большой размер исполняемого файла.

Catch2

Современный фреймворк с акцентом на читаемость тестов. Использует BDD-стиль (Behaviour-Driven Development) и поддерживает секции (sections).

Установка (header-only)

Catch2 распространяется как один заголовочный файл. Достаточно скачать catch_amalgamated.hpp и подключить его.

Пример с секциями

#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

int divide(int a, int b) {
    if (b == 0) throw std::invalid_argument("division by zero");
    return a / b;
}

TEST_CASE("Division tests", "[math]") {
    SECTION("Normal division") {
        REQUIRE(divide(10, 2) == 5);
    }
    SECTION("Division by zero") {
        REQUIRE_THROWS_AS(divide(5, 0), std::invalid_argument);
    }
}

BDD-стиль (Given-When-Then)

SCENARIO("Vector push_back operation", "[container]") {
    GIVEN("An empty vector") {
        std::vector<int> v;
        WHEN("We push one element") {
            v.push_back(42);
            THEN("The size becomes 1") {
                REQUIRE(v.size() == 1);
            }
            AND_THEN("The element is correct") {
                REQUIRE(v[0] == 42);
            }
        }
    }
}

Плюсы: очень читаемые тесты, header-only, быстрая компиляция, встроенный генератор тестовых данных.
Минусы: меньше макросов для сложных сценариев, слабее интеграция с death-тестами.

doctest

Лёгкий и

Рекомендаций