Введение: Что такое Catch2 и Google Test и зачем они нужны?
В современной разработке программного обеспечения на C++ тестирование — это не роскошь, а необходимость. Библиотеки для модульного тестирования (unit testing) позволяют разработчикам автоматически проверять, что отдельные компоненты программы (функции, классы, модули) работают корректно. Две самые популярные библиотеки для этой цели — Catch2 и Google Test (gtest).
Catch2 — это современная, легковесная и очень удобная библиотека для модульного тестирования. Её главная особенность — использование макросов, которые делают тесты читаемыми и похожими на обычный английский язык. Catch2 поддерживает Behaviour-Driven Development (BDD) стиль, автоматическую регистрацию тестов и не требует сложной конфигурации.
Google Test (gtest) — это мощная и зрелая библиотека от Google, которая является стандартом де-факто во многих крупных проектах. Она предлагает богатый набор assertion-макросов, механизмы для создания фикстур (test fixtures), параметризованные тесты и отличную интеграцию с системой сборки CMake.
Обе библиотеки решают одну и ту же задачу: позволяют писать тесты, которые автоматически запускаются и сообщают о результатах. Выбор между ними часто зависит от личных предпочтений, требований проекта или существующей инфраструктуры. В этой статье мы рассмотрим обе библиотеки, так как они очень похожи по концепции, но имеют различия в синтаксисе и API.
Установка
В отличие от Python (где используется pip), установка библиотек для C++ обычно происходит через менеджеры пакетов системы или через интеграцию с системой сборки (например, CMake). Рассмотрим оба варианта.
Установка Catch2
Способ 1: Через vcpkg (рекомендуется)
# Установите vcpkg, если он ещё не установлен
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh # или .\bootstrap-vcpkg.bat на Windows
# Установите Catch2
./vcpkg install catch2
# Интеграция с CMake
./vcpkg integrate install
Способ 2: Header-only (самый простой)
Catch2 можно использовать как header-only библиотеку. Просто скачайте файл catch.hpp с официального репозитория и положите его в папку с проектом. Затем подключите его в коде:
#define CATCH_CONFIG_MAIN // Эта строка должна быть только в одном .cpp файле
#include "catch.hpp"
Установка Google Test
Способ 1: Через vcpkg
./vcpkg install gtest
./vcpkg integrate install
Способ 2: Через CMake FetchContent (рекомендуется для проектов с CMake)
Добавьте в ваш CMakeLists.txt:
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
FetchContent_MakeAvailable(googletest)
# Теперь вы можете использовать target_link_libraries для вашего тестового файла
add_executable(my_tests test.cpp)
target_link_libraries(my_tests GTest::gtest_main)
Быстрый старт с примером кода
Давайте напишем простую функцию и протестируем её с помощью обеих библиотек. Функция будет вычислять факториал числа.
Пример с Catch2
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
// Функция для тестирования
unsigned int factorial(unsigned int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
// Тестовый кейс
TEST_CASE("Факториалы вычисляются корректно", "[factorial]") {
REQUIRE(factorial(0) == 1);
REQUIRE(factorial(1) == 1);
REQUIRE(factorial(2) == 2);
REQUIRE(factorial(3) == 6);
REQUIRE(factorial(10) == 3628800);
}
// Ещё один тестовый кейс с секциями
TEST_CASE("Проверка граничных случаев", "[factorial][edge]") {
SECTION("Факториал от 0") {
REQUIRE(factorial(0) == 1);
}
SECTION("Факториал от 1") {
REQUIRE(factorial(1) == 1);
}
}
Пример с Google Test
#include <gtest/gtest.h>
// Функция для тестирования
unsigned int factorial(unsigned int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
// Тестовый кейс
TEST(FactorialTest, HandlesZeroInput) {
EXPECT_EQ(factorial(0), 1);
}
TEST(FactorialTest, HandlesPositiveInput) {
EXPECT_EQ(factorial(1), 1);
EXPECT_EQ(factorial(2), 2);
EXPECT_EQ(factorial(3), 6);
EXPECT_EQ(factorial(10), 3628800);
}
// Главная функция для запуска тестов
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Основные методы и функции
Рассмотрим ключевые макросы и функции для обеих библиотек. Для Catch2 мы будем использовать макросы, для Google Test — макросы и функции.
1. TEST_CASE / TEST — Объявление тестового сценария
Что делает: Определяет новый тестовый кейс (группу тестов).
Параметры:
- Catch2: TEST_CASE("имя", "[тег1][тег2]")
- Google Test: TEST(TestSuiteName, TestName)
Пример:
// Catch2
TEST_CASE("Математические операции", "[math]") {
// тесты
}
// Google Test
TEST(MathTest, Addition) {
// тесты
}
2. REQUIRE / ASSERT_TRUE — Проверка условия (критическая)
Что делает: Проверяет, что условие истинно. Если условие ложно, тест немедленно прекращается (фатальная ошибка).
Параметры: Логическое выражение.
Пример:
// Catch2
REQUIRE(5 > 3); // Тест пройдёт
REQUIRE(2 + 2 == 5); // Тест упадёт здесь
// Google Test
ASSERT_TRUE(5 > 3);
ASSERT_TRUE(2 + 2 == 5); // Тест упадёт здесь
Что делает: Проверяет условие. Если условие ложно, тест продолжается, но ошибка фиксируется.
Параметры: Логическое выражение.
Пример:
// Catch2
CHECK(5 > 3); // Пройдёт
CHECK(2 + 2 == 5); // Ошибка, но тест продолжит выполнение
// Google Test
EXPECT_TRUE(5 > 3);
EXPECT_TRUE(2 + 2 == 5); // Ошибка, но тест продолжит выполнение
4. REQUIRE_EQ / EXPECT_EQ — Проверка равенства
Что делает: Проверяет, что два значения равны.
Параметры: REQUIRE_EQ(ожидаемое, фактическое)
Пример:
// Catch2
REQUIRE_EQ(4, 2 + 2); // Пройдёт
REQUIRE_EQ(5, 2 + 2); // Упадёт
// Google Test
EXPECT_EQ(4, 2 + 2);
ASSERT_EQ(5, 2 + 2); // Упадёт
5. REQUIRE_NE / EXPECT_NE — Проверка неравенства
Что делает: Проверяет, что два значения не равны.
Параметры: REQUIRE_NE(значение1, значение2)
Пример:
// Catch2
REQUIRE_NE(5, 3); // Пройдёт
REQUIRE_NE(4, 2 + 2); // Упадёт
// Google Test
EXPECT_NE(5, 3);
ASSERT_NE(4, 2 + 2); // Упадёт
6. REQUIRE_THROWS / EXPECT_THROW — Проверка исключений
Что делает: Проверяет, что код выбрасывает исключение определённого типа.
Параметры: REQUIRE_THROWS_AS(выражение, тип_исключения)
Пример:
#include <stdexcept>
void mightThrow(bool should) {
if (should) throw std::runtime_error("Ошибка!");
}
// Catch2
TEST_CASE("Проверка исключений", "[exceptions]") {
REQUIRE_THROWS_AS(mightThrow(true), std::runtime_error);
REQUIRE_NOTHROW(mightThrow(false));
}
// Google Test
TEST(ExceptionTest, ThrowsRuntimeError) {
EXPECT_THROW(mightThrow(true), std::runtime_error);
EXPECT_NO_THROW(mightThrow(false));
}
7. SECTION / Test Fixtures (SetUp/TearDown) — Группировка тестов
Что делает: Позволяет структурировать тесты и выполнять общую инициализацию/очистку.
Параметры: В Catch2 — SECTION("имя"), в Google Test — класс, наследующий от ::testing::Test.
Пример (Catch2):
TEST_CASE("Работа с вектором", "[vector]") {
std::vector<int> v = {1, 2, 3};
SECTION("Проверка размера") {
REQUIRE(v.size() == 3);
}
SECTION("Проверка доступа") {
REQUIRE(v[0] == 1);
REQUIRE(v[2] == 3);
}
}
Пример (Google Test):
class VectorTest : public ::testing::Test {
protected:
void SetUp() override {
v = {1, 2, 3};
}
void TearDown() override {
// Очистка, если нужна
}
std::vector<int> v;
};
TEST_F(VectorTest, SizeIsCorrect) {
EXPECT_EQ(v.size(), 3);
}
TEST_F(VectorTest, AccessIsCorrect) {
EXPECT_EQ(v[0], 1);
EXPECT_EQ(v[2], 3);
}
8. GENERATE / ValuesIn — Параметризованные тесты
Что делает: Позволяет запускать один и тот же тест с разными входными данными.
Параметры: Список значений.
Пример (Catch2):
TEST_CASE("Параметризованный тест", "[param]") {
auto data = GENERATE(1, 2, 3, 4, 5);
REQUIRE(data < 10);
}
Пример (Google Test):
class ParamTest : public ::testing::TestWithParam<int> {};
TEST_P(ParamTest, IsLessThanTen) {
int value = GetParam();
EXPECT_LT(value, 10);
}
INSTANTIATE_TEST_SUITE_P(Values, ParamTest, ::testing::Values(1, 2, 3, 4, 5));
9. CHECK_THAT / EXPECT_THAT — Проверка с использованием Matchers
Что делает: Использует мощные сопоставители (matchers) для сложных проверок.
Параметры: Выражение и matcher.
Пример (Catch2):
#include <catch2/matchers/catch_matchers_string.hpp>
TEST_CASE("Проверка строк", "[string]") {
std::string text = "Hello, World!";
CHECK_THAT(text, Catch::Matchers::Contains