Testing in C++: Overview of Frameworks and Tools (Google Test, Catch2, doctest)

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Testing in C++: Frameworks and Tools



Quality testing is an essential part of C++ development. Unlike dynamic languages, C++ requires special attention to memory management, object lifetimes, and performance. In this article, we will cover three of the most popular unit testing frameworks: Google Test, Catch2, and doctest, as well as touch on additional tools.



Why is testing needed in C++?

C++ is a language with manual memory management and powerful template magic. Without tests, it is easy to introduce:

  • Memory leaks and double free
  • Undefined behavior (UB) when working with pointers
  • Errors in operator overloading and copy constructors
  • Regressions when refactoring legacy code

Testing frameworks automate verification and help quickly find problems.



Google Test (gtest)

The most common framework from Google. It supports all major features: ASSERT_* and EXPECT_* macros, parameterized tests, test fixtures, death tests.



Installation (via CMake FetchContent)

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


Simple test example

#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();}


Fixtures and parameterized tests

class MathFixture : public ::testing::Test {protected:    void SetUp() override { /* initialization */ }    void TearDown() override { /* cleanup */ }};

// Parameterized testclass 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) ));


Pros: huge community, CI/CD integration, powerful macros, death test support.
Cons: relatively slow compilation, large executable file size.



Catch2

A modern framework with an emphasis on test readability. It uses BDD-style (Behaviour-Driven Development) and supports sections.



Installation (header-only)

Catch2 is distributed as a single header file. Simply download catch_amalgamated.hpp and include it.



Example with sections

#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-style (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);                    

Recommendations