Docs

README

Unit Testing with Google Test

Overview

Google Test (gtest) is the most popular C++ testing framework. It provides a rich set of assertions, test fixtures, and features for writing comprehensive unit tests.

Learning Objectives

By the end of this section, you will understand:

  • Setting up Google Test
  • Writing test cases and test suites
  • Using assertions and expectations
  • Creating test fixtures
  • Parameterized tests
  • Mocking with Google Mock

Why Unit Testing?

┌─────────────────────────────────────────────────────────────────────────┐
│                    TESTING PYRAMID                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│                         ▲                                               │
│                        /│\         End-to-End Tests                     │
│                       / │ \        (Few, Slow, Expensive)               │
│                      /  │  \                                            │
│                     /───┼───\                                           │
│                    /    │    \     Integration Tests                    │
│                   /     │     \    (Some, Medium Speed)                 │
│                  /──────┼──────\                                        │
│                 /       │       \                                       │
│                /        │        \  Unit Tests                          │
│               /─────────┼─────────\ (Many, Fast, Cheap)                 │
│              ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔                                    │
│                                                                         │
│  Unit tests form the foundation - fast feedback, isolated tests         │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Setting Up Google Test

Using FetchContent (Recommended)

cmake_minimum_required(VERSION 3.16)
project(MyProject)

include(FetchContent)
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(tests test_main.cpp)
target_link_libraries(tests GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(tests)

Installing on Linux

# Ubuntu/Debian
sudo apt install libgtest-dev

# Build if needed
cd /usr/src/gtest
sudo cmake .
sudo make
sudo cp lib/*.a /usr/lib

Basic Test Structure

#include <gtest/gtest.h>

// Simple test
TEST(TestSuiteName, TestName) {
    // Arrange
    int a = 2;
    int b = 3;

    // Act
    int result = a + b;

    // Assert
    EXPECT_EQ(result, 5);
}

Assertions

┌─────────────────────────────────────────────────────────────────────────┐
│                    ASSERTION TYPES                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  EXPECT_* : Non-fatal assertion (test continues on failure)     │   │
│  │  ASSERT_* : Fatal assertion (test stops on failure)             │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  Boolean:                                                               │
│  ─────────────────────────────────────────────────                      │
│  EXPECT_TRUE(condition)                                                 │
│  EXPECT_FALSE(condition)                                                │
│                                                                         │
│  Comparison:                                                            │
│  ─────────────────────────────────────────────────                      │
│  EXPECT_EQ(val1, val2)       // val1 == val2                           │
│  EXPECT_NE(val1, val2)       // val1 != val2                           │
│  EXPECT_LT(val1, val2)       // val1 < val2                            │
│  EXPECT_LE(val1, val2)       // val1 <= val2                           │
│  EXPECT_GT(val1, val2)       // val1 > val2                            │
│  EXPECT_GE(val1, val2)       // val1 >= val2                           │
│                                                                         │
│  Floating Point:                                                        │
│  ─────────────────────────────────────────────────                      │
│  EXPECT_FLOAT_EQ(val1, val2)                                           │
│  EXPECT_DOUBLE_EQ(val1, val2)                                          │
│  EXPECT_NEAR(val1, val2, abs_error)                                    │
│                                                                         │
│  String:                                                                │
│  ─────────────────────────────────────────────────                      │
│  EXPECT_STREQ(str1, str2)         // C strings equal                   │
│  EXPECT_STRNE(str1, str2)         // C strings not equal               │
│  EXPECT_STRCASEEQ(str1, str2)     // Case-insensitive equal            │
│                                                                         │
│  Exception:                                                             │
│  ─────────────────────────────────────────────────                      │
│  EXPECT_THROW(statement, exception_type)                                │
│  EXPECT_ANY_THROW(statement)                                            │
│  EXPECT_NO_THROW(statement)                                             │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Test Fixtures

Test fixtures allow you to share setup and teardown code between tests:

class CalculatorTest : public ::testing::Test {
protected:
    Calculator calc;  // Shared across tests

    void SetUp() override {
        // Called before each test
        calc.clear();
    }

    void TearDown() override {
        // Called after each test
    }
};

TEST_F(CalculatorTest, Addition) {
    EXPECT_EQ(calc.add(2, 3), 5);
}

TEST_F(CalculatorTest, Subtraction) {
    EXPECT_EQ(calc.subtract(5, 3), 2);
}

Test Organization

┌─────────────────────────────────────────────────────────────────────────┐
│                    TEST ORGANIZATION                                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Project                                                                │
│  └── tests/                                                             │
│      ├── CMakeLists.txt                                                 │
│      ├── main.cpp           (if not using gtest_main)                   │
│      ├── test_calculator.cpp                                            │
│      ├── test_string_utils.cpp                                          │
│      └── test_database.cpp                                              │
│                                                                         │
│  Naming Convention:                                                     │
│  ─────────────────────────────────────────────────                      │
│  TEST(SuiteName, TestName)                                              │
│  │         │        │                                                   │
│  │         │        └─── What is being tested                          │
│  │         └───────────── Class or feature being tested                │
│  └─────────────────────── TEST macro                                   │
│                                                                         │
│  Examples:                                                              │
│  TEST(Calculator, AddsTwoPositiveNumbers)                               │
│  TEST(Calculator, ReturnsZeroForEmptyInput)                             │
│  TEST(StringUtils, TrimsWhitespace)                                     │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Parameterized Tests

Test the same logic with different inputs:

class MultiplicationTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {
};

TEST_P(MultiplicationTest, Multiplies) {
    auto [a, b, expected] = GetParam();
    EXPECT_EQ(a * b, expected);
}

INSTANTIATE_TEST_SUITE_P(
    MultiplicationTests,
    MultiplicationTest,
    ::testing::Values(
        std::make_tuple(2, 3, 6),
        std::make_tuple(0, 5, 0),
        std::make_tuple(-2, 3, -6),
        std::make_tuple(-2, -3, 6)
    )
);

Typed Tests

Test the same logic with different types:

template <typename T>
class ContainerTest : public ::testing::Test {
public:
    using Container = T;
};

using ContainerTypes = ::testing::Types<std::vector<int>, std::list<int>, std::deque<int>>;
TYPED_TEST_SUITE(ContainerTest, ContainerTypes);

TYPED_TEST(ContainerTest, StartsEmpty) {
    TypeParam container;
    EXPECT_TRUE(container.empty());
}

TYPED_TEST(ContainerTest, SizeIncreases) {
    TypeParam container;
    container.push_back(1);
    EXPECT_EQ(container.size(), 1);
}

Google Mock

Mock objects for testing dependencies:

#include <gmock/gmock.h>

// Interface
class Database {
public:
    virtual ~Database() = default;
    virtual bool connect() = 0;
    virtual std::string query(const std::string& sql) = 0;
};

// Mock
class MockDatabase : public Database {
public:
    MOCK_METHOD(bool, connect, (), (override));
    MOCK_METHOD(std::string, query, (const std::string& sql), (override));
};

// Test using mock
TEST(UserServiceTest, LoadsUserFromDatabase) {
    MockDatabase mockDb;

    EXPECT_CALL(mockDb, connect())
        .WillOnce(::testing::Return(true));

    EXPECT_CALL(mockDb, query("SELECT * FROM users WHERE id = 1"))
        .WillOnce(::testing::Return("{\"name\": \"John\"}"));

    UserService service(&mockDb);
    User user = service.loadUser(1);

    EXPECT_EQ(user.name, "John");
}

Best Practices

┌─────────────────────────────────────────────────────────────────────────┐
│                    TESTING BEST PRACTICES                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ✓ DO                                                                   │
│  ─────────────────────────────────────────────────                      │
│  • Write tests first (TDD) or alongside code                           │
│  • Test one thing per test                                              │
│  • Use descriptive test names                                           │
│  • Test edge cases (empty, null, max values)                           │
│  • Keep tests fast (< 1 second each)                                   │
│  • Make tests independent (no shared state)                            │
│  • Use fixtures for common setup                                        │
│  • Test both success and failure paths                                  │
│                                                                         │
│  ✗ DON'T                                                                │
│  ─────────────────────────────────────────────────                      │
│  • Test private methods directly                                        │
│  • Make tests depend on each other                                      │
│  • Use magic numbers without explanation                                │
│  • Test trivial getters/setters                                        │
│  • Write flaky tests (pass/fail randomly)                              │
│  • Ignore failed tests                                                  │
│                                                                         │
│  Arrange-Act-Assert (AAA) Pattern:                                      │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  TEST(Calculator, AddsNumbers) {                                │   │
│  │      // Arrange - set up test data                              │   │
│  │      Calculator calc;                                           │   │
│  │      int a = 5, b = 3;                                          │   │
│  │                                                                  │   │
│  │      // Act - perform the action                                │   │
│  │      int result = calc.add(a, b);                               │   │
│  │                                                                  │   │
│  │      // Assert - verify the result                              │   │
│  │      EXPECT_EQ(result, 8);                                      │   │
│  │  }                                                               │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Running Tests

# Build and run all tests
cmake --build build
ctest --test-dir build

# Run with verbose output
ctest --test-dir build -V

# Run specific test
./build/tests --gtest_filter="CalculatorTest.*"

# Run tests matching pattern
./build/tests --gtest_filter="*Add*"

# List all tests
./build/tests --gtest_list_tests

# Run with shuffled order (find dependencies)
./build/tests --gtest_shuffle

# Repeat tests (find flaky tests)
./build/tests --gtest_repeat=10

# Output XML report
./build/tests --gtest_output=xml:report.xml

Summary

ConceptPurpose
TEST()Define a simple test
TEST_F()Test using a fixture
EXPECT_*Non-fatal assertions
ASSERT_*Fatal assertions
SetUp/TearDownTest fixture lifecycle
TEST_PParameterized tests
TYPED_TESTType-parameterized tests
MOCK_METHODCreate mock methods

Next Steps

  • Practice with examples.cpp
  • Complete the exercises.cpp challenges
  • Try writing tests for your own projects
  • Explore Google Mock for dependency injection
README - C++ Tutorial | DeepML