Module 34: Unit Testing in C++
Introduction
Unit Testing is the process of testing individual functions, classes, or modules to verify that they work correctly in isolation. Instead of manually checking program output, unit tests automatically validate expected behavior.
Unit testing helps developers:
- Detect bugs early
- Prevent regressions
- Improve code quality
- Refactor code safely
- Increase confidence before deployment
Modern C++ projects commonly use:
- Google Test (GTest)
- Google Mock (GMock)
- Catch2
Learning Objectives
After completing this module, you will understand:
- What is Unit Testing?
- Google Test (GTest)
- Catch2
- Assertions
- Test Cases
- Test Fixtures
- Mocking
- Test-Driven Development (TDD)
- Best Practices
What is Unit Testing?
A unit test verifies that a single unit of code behaves as expected.
1Write Function 2 │ 3 ▼ 4Write Test 5 │ 6 ▼ 7Run Tests 8 │ 9 ▼ 10Pass ✔ / Fail ✖
Why Unit Testing?
Without Testing
1Write Code 2 3↓ 4 5Deploy 6 7↓ 8 9Unexpected Bugs
With Testing
1Write Code 2 3↓ 4 5Run Tests 6 7↓ 8 9Fix Bugs 10 11↓ 12 13Deploy
Installing Google Test
Ubuntu
1sudo apt install libgtest-dev cmake
Build Google Test
1cd /usr/src/gtest 2sudo cmake . 3sudo make 4sudo cp lib/*.a /usr/lib
Basic Google Test Structure
1#include <gtest/gtest.h> 2 3TEST(TestSuiteName, TestName) 4{ 5 // Test code 6} 7 8int main(int argc, char **argv) 9{ 10 ::testing::InitGoogleTest(&argc, argv); 11 return RUN_ALL_TESTS(); 12}
1. Google Test
Google Test is Google's official C++ testing framework.
Function to Test
1int add(int a, int b) 2{ 3 return a + b; 4}
Google Test Example
1#include <gtest/gtest.h> 2 3int add(int a, int b) 4{ 5 return a + b; 6} 7 8TEST(MathTest, Addition) 9{ 10 EXPECT_EQ(add(2,3),5); 11} 12 13int main(int argc, char **argv) 14{ 15 ::testing::InitGoogleTest(&argc, argv); 16 return RUN_ALL_TESTS(); 17}
Output
1[==========] Running 1 test 2[ RUN ] MathTest.Addition 3[ OK ] MathTest.Addition 4[==========] 1 test passed
Multiple Tests
1#include <gtest/gtest.h> 2 3int square(int n) 4{ 5 return n * n; 6} 7 8TEST(MathTest, SquarePositive) 9{ 10 EXPECT_EQ(square(4),16); 11} 12 13TEST(MathTest, SquareZero) 14{ 15 EXPECT_EQ(square(0),0); 16} 17 18TEST(MathTest, SquareNegative) 19{ 20 EXPECT_EQ(square(-5),25); 21}
Common Google Test Assertions
| Assertion | Description |
|---|---|
| EXPECT_EQ(a,b) | Equal |
| EXPECT_NE(a,b) | Not equal |
| EXPECT_GT(a,b) | Greater than |
| EXPECT_LT(a,b) | Less than |
| EXPECT_TRUE(x) | True |
| EXPECT_FALSE(x) | False |
| ASSERT_EQ(a,b) | Fatal equality check |
EXPECT vs ASSERT
EXPECT
Test continues after failure.
1TEST(SampleTest, ExpectExample) 2{ 3 EXPECT_EQ(5,4); 4 5 EXPECT_TRUE(true); 6}
ASSERT
Test stops immediately on failure.
1TEST(SampleTest, AssertExample) 2{ 3 ASSERT_EQ(5,4); 4 5 EXPECT_TRUE(true); 6}
2. Catch2
Catch2 is a lightweight C++ testing framework that is easy to integrate.
Basic Catch2 Test
1#include <catch2/catch_test_macros.hpp> 2 3int multiply(int a,int b) 4{ 5 return a * b; 6} 7 8TEST_CASE("Multiplication Test") 9{ 10 REQUIRE(multiply(4,5) == 20); 11}
Multiple Sections
1#include <catch2/catch_test_macros.hpp> 2 3TEST_CASE("Division") 4{ 5 SECTION("Positive") 6 { 7 REQUIRE(10/2 == 5); 8 } 9 10 SECTION("Negative") 11 { 12 REQUIRE(-10/2 == -5); 13 } 14}
Catch2 Assertions
| Assertion | Description |
|---|---|
| REQUIRE | Stops current test section |
| CHECK | Continues execution |
| REQUIRE_FALSE | Requires false |
| REQUIRE_THROWS | Expects an exception |
3. C++ Assertions
The standard library also provides runtime assertions.
1#include <cassert> 2 3int factorial(int n) 4{ 5 assert(n >= 0); 6 7 if(n == 0) 8 return 1; 9 10 return n * factorial(n - 1); 11}
Assertion Example
1#include <cassert> 2 3int main() 4{ 5 int age = 20; 6 7 assert(age > 0); 8}
4. Test Cases
Every function should have multiple test cases.
Example Function
1int maximum(int a,int b) 2{ 3 return (a > b) ? a : b; 4}
Test Cases
1#include <gtest/gtest.h> 2 3int maximum(int a,int b) 4{ 5 return (a > b) ? a : b; 6} 7 8TEST(MaximumTest, Positive) 9{ 10 EXPECT_EQ(maximum(8,3),8); 11} 12 13TEST(MaximumTest, Equal) 14{ 15 EXPECT_EQ(maximum(5,5),5); 16} 17 18TEST(MaximumTest, Negative) 19{ 20 EXPECT_EQ(maximum(-4,-2),-2); 21}
Test Fixture
A fixture shares common setup and cleanup across multiple tests.
1#include <gtest/gtest.h> 2#include <vector> 3 4class VectorTest : public ::testing::Test 5{ 6protected: 7 std::vector<int> values; 8 9 void SetUp() override 10 { 11 values = {1,2,3}; 12 } 13}; 14 15TEST_F(VectorTest, Size) 16{ 17 EXPECT_EQ(values.size(),3); 18} 19 20TEST_F(VectorTest, FirstElement) 21{ 22 EXPECT_EQ(values.front(),1); 23}
5. Mocking
Mocking replaces real dependencies with fake objects.
Example:
1Application 2 3↓ 4 5Mock Database 6 7↓ 8 9Testing
Instead of connecting to a real database, a mock object returns predefined values.
Interface
1class Database 2{ 3public: 4 5 virtual int getAge() = 0; 6 7 virtual ~Database() = default; 8};
Google Mock Example
1#include <gmock/gmock.h> 2 3class MockDatabase : public Database 4{ 5public: 6 7 MOCK_METHOD(int, getAge, (), (override)); 8};
Mock Test
1#include <gtest/gtest.h> 2#include <gmock/gmock.h> 3 4using ::testing::Return; 5 6TEST(DatabaseTest, MockAge) 7{ 8 MockDatabase db; 9 10 EXPECT_CALL(db, getAge()) 11 .WillOnce(Return(25)); 12 13 EXPECT_EQ(db.getAge(),25); 14}
Testing Exceptions
1#include <gtest/gtest.h> 2#include <stdexcept> 3 4int divide(int a,int b) 5{ 6 if(b == 0) 7 throw std::runtime_error("Divide by zero"); 8 9 return a / b; 10} 11 12TEST(ExceptionTest, DivideByZero) 13{ 14 EXPECT_THROW(divide(10,0), std::runtime_error); 15}
Parameterized Tests
1#include <gtest/gtest.h> 2 3class EvenTest : 4 public ::testing::TestWithParam<int> 5{ 6}; 7 8TEST_P(EvenTest, CheckEven) 9{ 10 EXPECT_EQ(GetParam() % 2,0); 11} 12 13INSTANTIATE_TEST_SUITE_P( 14 Values, 15 EvenTest, 16 ::testing::Values(2,4,6,8) 17);
Test-Driven Development (TDD)
1Write Test 2 3↓ 4 5Run Test (Fail) 6 7↓ 8 9Write Code 10 11↓ 12 13Run Test (Pass) 14 15↓ 16 17Refactor
Code Coverage
Code coverage measures how much of the source code is executed during testing.
Common metrics:
| Metric | Description |
|---|---|
| Line Coverage | Executed lines |
| Branch Coverage | Executed decision branches |
| Function Coverage | Executed functions |
Best Practices
- Write small, independent tests.
- Test one behavior per test case.
- Use meaningful test names.
- Cover edge cases and invalid inputs.
- Prefer mocks over external services in unit tests.
- Keep tests deterministic and repeatable.
- Run tests automatically in CI/CD pipelines.
Common Testing Mistakes
Testing Multiple Features Together
Each unit test should verify a single behavior.
Ignoring Edge Cases
Test empty containers, zero values, negative values, and boundary conditions.
Depending on External Resources
Avoid relying on databases, networks, or files in unit tests. Use mocks or test doubles instead.
Poor Test Names
Use descriptive names such as:
1MathTest.AdditionWithPositiveNumbers
instead of
1Test1
Google Test vs Catch2
| Feature | Google Test | Catch2 |
|---|---|---|
| Assertions | Rich set | Simple API |
| Mocking | Google Mock | External libraries |
| Fixtures | Yes | Yes |
| Parameterized Tests | Yes | Limited |
| Ease of Setup | Moderate | Easy |
| Common Usage | Large projects | Small to medium projects |
Real-World Applications
| Area | Usage |
|---|---|
| Banking | Validate financial calculations |
| Game Development | Test physics and gameplay logic |
| AI/ML | Verify preprocessing and utility functions |
| Embedded Systems | Test hardware abstraction layers |
| Web Services | Validate business logic and APIs |
Interview Questions
1. What is unit testing?
Testing individual functions or classes in isolation to verify their correctness.
2. What is Google Test?
A widely used C++ framework for writing and running automated unit tests.
3. What is the difference between EXPECT_EQ and ASSERT_EQ?
EXPECT_EQrecords a failure and continues the test.ASSERT_EQrecords a failure and immediately stops the current test.
4. What is Catch2?
A lightweight, header-friendly C++ testing framework with a simple syntax.
5. What is mocking?
Replacing real dependencies with simulated objects to isolate the code under test.
6. What is a test fixture?
A reusable setup and teardown mechanism shared by multiple related tests.
7. What is TDD?
Test-Driven Development is a workflow where tests are written before the implementation.
8. Why is code coverage important?
It helps identify untested parts of the codebase, though high coverage alone does not guarantee bug-free software.
Module Summary
In this module, you learned:
- The fundamentals and benefits of unit testing
- Writing tests with Google Test (GTest)
- Using Catch2 for lightweight testing
- Standard C++ assertions with
<cassert> - Creating clear and maintainable test cases
- Sharing setup with test fixtures
- Isolating dependencies using Google Mock (GMock)
- Testing exceptions and parameterized inputs
- Following Test-Driven Development (TDD) practices
- Best practices for building reliable, maintainable C++ software through automated testing