Testing in C++: Unit, Integration, E2E from A to Z

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



In the world of C++, code quality is crucial. The language gives the developer immense power over memory and performance, but this power comes with great responsibility. An error in memory management, incorrect use of pointers, or improper handling of multithreading can lead to hard-to-find bugs that only manifest in production.



Testing is the only reliable way to ensure your C++ code behaves as intended. In this article, we will journey from small unit tests to complete end-to-end (E2E) checks. You will learn how to properly build a testing strategy, which tools to use, and how to write tests that truly provide value.



We will examine three levels of the testing pyramid: unit, integration, and e2e. Each one solves its own task, and only their combination provides complete confidence in the program's reliability.



Unit Testing: The Foundation of Quality



Unit tests are the lowest and fastest level. They check individual functions, classes, or methods in isolation from external dependencies (databases, file systems, network). The main goal is to ensure that every logical unit of code works correctly.



Choosing a Framework for C++



The C++ ecosystem has several popular frameworks for unit testing:



  • Google Test (gtest) — the most common framework from Google. It supports assertions, fixtures, parameterized tests, and mocks (via Google Mock).
  • Catch2 — a lightweight and modern framework with excellent syntax. It is especially good for rapid test prototyping.
  • Boost.Test — part of the Boost library. Powerful, but more heavyweight.


For the example, we will use Google Test, as it is the de facto standard in many C++ projects.



Example of a Unit Test in C++



Suppose we have a simple function for calculating a factorial:



// math_utils.h#ifndef MATH_UTILS_H#define MATH_UTILS_H

long long factorial(int n);

#endif

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

long long factorial(int n) { if (n < 0) throw std::invalid_argument("Negative input"); if (n == 0) return 1; long long result = 1; for (int i = 1; i <= n; ++i) { result *= i; } return result;}


Now let's write a unit test using Google Test:



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

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

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

TEST(FactorialTest, ThrowsOnNegative) { EXPECT_THROW(factorial(-1), std::invalid_argument);}


This test checks three scenarios: the edge case (0), normal operation, and error handling. Note the macros EXPECT_EQ and EXPECT_THROW — they do not interrupt the test on failure, but merely record it.



Mock Objects for Isolation



For unit testing, it is critically important to isolate the code under test from external dependencies. Mock objects are used for this purpose. Google Mock (part of Google Test) allows you to easily create stubs:



// database_connector.hclass DatabaseConnector {public:    virtual ~DatabaseConnector() = default;    virtual bool connect(const std::string& url) = 0;    virtual std::vector<int> fetchData() = 0;};

// user_service.cppclass UserService { DatabaseConnector& db;public: UserService(DatabaseConnector& db) : db(db) {} int ge

Blogs

Book Recommendations