Testing in C++: Unit, Integration and E2E. A Complete Guide

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

Introduction: Why Testing in C++ Is Not a Luxury, but a Necessity



Development in C++ is always a balance between performance and reliability. Memory management errors, undefined behavior (UB), and complex dependencies between modules make C++ one of the most demanding languages in terms of code quality. Without a systematic approach to testing, a project quickly turns into a "black box," where fixing one bug spawns two new ones.



In this article, we will break down three key levels of testing in C++: unit tests, integration tests, and e2e tests (end-to-end). You will learn which tools to use, how to write effective tests, and how to build a testing pyramid for a C++ project.



1. Unit Testing: Checking Every Brick



Unit tests are the foundation of the testing pyramid. They isolate and verify a single logical unit of code: a function, a class method, or a small structure. In C++, unit tests must execute quickly (<1 second for hundreds of tests) and must not depend on external resources (file system, network, databases).



Tools for C++ Unit Testing

  • Google Test (gtest) — the most popular framework from Google. It supports assertion macros, fixtures, and parameterized tests.
  • Catch2 — a modern alternative with concise syntax and support for BDD style.
  • Boost.Test — part of the Boost library, a mature tool with extensive capabilities.


Example of a Unit Test in Google Test

Suppose we have a function for calculating factorial:



// math_utils.h#pragma onceunsigned long long factorial(unsigned int n);


// math_utils.cpp#include "math_utils.h"

unsigned long long factorial(unsigned int n) { if (n == 0) return 1; unsigned long long result = 1; for (unsigned int i = 1; i <= n; ++i) { result *= i; } return result;}


We write a unit test:



// test_math_utils.cpp#include <gtest/gtest.h>#include "math_utils.h"

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

TEST(FactorialTest, HandlesPositiveNumbers) { EXPECT_EQ(factorial(1), 1); EXPECT_EQ(factorial(5), 120); EXPECT_EQ(factorial(10), 3628800);}

TEST(FactorialTest, HandlesLargeInput) { // 20! fits in unsigned long long EXPECT_EQ(factorial(20), 2432902008176640000ULL);}


Best practices for unit testing in C++:

  • One test — one check. Use EXPECT_EQ for soft checks and ASSERT_EQ for critical checks.
  • Isolate dependencies using mock objects (Google Mock).
  • Test edge cases: zero values, overflow, empty containers.
  • Use TEST_F for repeated initialization (fixtures).


2. Integration Testing: How Modules Work Together



Integration tests verify the interaction between multiple modules or components. In C++, this is especially important because errors often occur at the boundaries: incorrect memory management when passing objects, interface mismatches, or multithreading issues.



Unlike unit tests, integration tests can work with real databases, files, or network sockets, but typically in a test environment (e.g., temporary files or Docker containers).



Example of an Integration Test

Suppose we have a Logger module and a FileManager module. We want to verify that logs are correctly written to a file.



// file_logger.h#pragma once#include <string>

class FileLogger {public: FileLogger(const std::string& filename); vo

Blogs

Book Recommendations