Introduction: Why C++ testing is the foundation of reliable code
\nDeveloping in C++ always involves a high degree of responsibility. The language provides full control over memory and performance, but the cost of an error can be catastrophic: from memory leaks to a complete system crash. That is why testing in C++ is not an option, but a mandatory stage in the development of any serious project.
\nIn this article, we will cover three key levels of testing: unit, integration, and e2e (end-to-end). You will learn how they differ, which tools to use, and how to write tests in C++ correctly. The material will be useful for both beginners and experienced developers looking to systematize their knowledge.
\n\n1. Unit testing in C++: Testing the "building blocks" of the program
\nUnit tests check the smallest unit of code — usually a single function or class method. Their main goal is to isolate a component and ensure it works correctly in isolation from external dependencies.
\n\n1.1. Tools for C++ unit testing
\nThere are several mature frameworks on the C++ market. Here are the most popular ones:
\n- \n
- Google Test (GTest) — the de facto standard. Powerful macros, support for parameterized tests, mock objects (via Google Mock). \n
- Catch2 — lightweight, modern, with excellent syntax. Suitable for small projects. \n
- Boost.Test — part of the Boost library. Bulky but functional. \n
We will use Google Test as the most common option.
\n\n1.2. Example of a unit test in C++ (Google Test)
\nSuppose we have a simple function for calculating a factorial:
\n// math_utils.h\n#pragma once\nint factorial(int n);\n// math_utils.cpp\n#include "math_utils.h"\nint factorial(int n) {\n if (n < 0) return -1; // error: negative number\n if (n == 0) return 1;\n return n * factorial(n - 1);\n}\nNow let's write a unit test using Google Test:
\n// test_math_utils.cpp\n#include <gtest/gtest.h>\n#include "math_utils.h"\n\nTEST(FactorialTest, HandlesZeroInput) {\n EXPECT_EQ(factorial(0), 1);\n}\n\nTEST(FactorialTest, HandlesPositiveInput) {\n EXPECT_EQ(factorial(5), 120);\n}\n\nTEST(FactorialTest, HandlesNegativeInput) {\n EXPECT_EQ(factorial(-1), -1);\n}\n\nint main(int argc, char **argv) {\n ::testing::InitGoogleTest(&argc, argv);\n return RUN_ALL_TESTS();\n}\nWe run the tests — and see a green light. If there were an error in the function (for example, factorial(5) returned 119), the test would fail, and we would immediately know about the problem.
1.3. Best practices for unit tests
\n- \n
- Isolation: Use mocks to replace external dependencies (DB, network, file system). \n
- One assertion per test: Each
TESTshould verify one behavior. \n - Clear names:
HandlesZeroInputis better thanTest1. \n - Cover edge cases: Zero, negative numbers, maximums. \n
2. Integration Testing: Checking the interaction
\nIntegration tests check how different modules of the system work together. Unlike unit tests, here we do not isolate components — we check real connections (for example, calling a function from one class that uses another class).
\n\n2.1. When is integration testing needed?
\n- \n
- When two modules exchange data (e.g., parser + validator). \n
- When working with external resources: database, files, network sockets. \n
- After combining several unit-tested components. \n