Introduction: Why do we write bugs and then heroically fix them?
\n\nHave you ever written code that worked "sort of okay," only to find a bug three days later that broke everything? If yes — welcome to the club. If no — you are either a C++ god, or you just haven't deployed to production yet.
\n\nTest-Driven Development (TDD) is not a trendy diet for programmers. It is a discipline that turns the coding process upside down. Instead of writing the logic first and then frantically figuring out how to test it, we do the opposite: test first, then code. Sounds crazy? Maybe. But this is exactly how code is born that you're not ashamed to show your colleagues and that doesn't break on a Friday evening.
\n\nIn this article, we'll break down TDD in C++ with real examples: we'll write a simple library for working with Fibonacci numbers using the Red-Green-Refactor cycle. You'll see how TDD helps not only to avoid bugs but also to design interfaces.
\n\nWhat is TDD and why is C++ without it like driving without brakes?
\n\nTDD (Test-Driven Development) is a development methodology where you first write a test that is expected to fail (Red), then write the minimal code to make the test pass (Green), and finally refactor (Refactor) to improve the structure without changing the behavior.
\n\nWhy is this especially important for C++? Because C++ is a language where it's easy to shoot yourself in the foot: undefined behavior, memory leaks, incorrect type conversions. TDD automatically creates a safety net that catches errors at compile time and runtime.
\n\nThe Red-Green-Refactor Cycle: How it works
\n\n- \n
- Red: write a test that should fail. This is our "requirement" for the code. \n
- Green: write the minimal code to make the test pass. No over-engineering! \n
- Refactor: improve the code (remove duplication, simplify logic) — the tests remain green. \n
Sounds simple? Yes. But in practice, it requires discipline. Let's try it.
\n\nTools for TDD in C++: Google Test vs Catch2
\n\nBefore writing code, let's choose a testing framework. Two main candidates:
\n\n- \n
- Google Test — powerful, popular, with tons of macros. Great for large projects. \n
- Catch2 — lightweight, modern, with convenient syntax. Ideal for tutorials and microservices. \n
In this article, we'll use Catch2 (v3) because it's minimalistic and doesn't require extra setup. But if you prefer Google Test — the TDD principles are the same.
\n\nPractice: Writing TDD for Fibonacci Numbers
\n\nLet's imagine we need to write a function fibonacci(int n) that returns the n-th Fibonacci number (0th = 0, 1st = 1, 2nd = 1, 3rd = 2, etc.).
Step 1: Red — write a test that fails
\n\nCreate a file test_fib.cpp:
#define CATCH_CONFIG_MAIN\n#include <catch2/catch_test_macros.hpp>\n\n// The function is not declared yet — that's fine\nint fibonacci(int n);\n\nTEST_CASE("Fibonacci numbers", "[fib]") {\n REQUIRE(fibonacci(0) == 0);\n REQUIRE(fibonacci(1) == 1);\n REQUIRE(fibonacci(2) == 1);\n REQUIRE(fibonacci(10) == 55);\n}\n\nTry to compile — we get a linker error. The test is RED. Excellent!
\n\nStep 2: Green — write the minimal code to make the test pass
\n\nCreate a file fib.cpp:
int fibonacci(int n) {\n if (n <= 1) return n;\n return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nCompile and run the tests. Do they pass? Yes, but it's inefficient. However, our goal is Green, not perfection. We'll leave it as is for now.
\n\n