CODE: Google Test — Unit Testing Framework for C++

Google Test (GTest) is the most widely used unit testing framework for C++. It provides a clean, expressive API for defining test cases and suites, a rich set of assertion macros for verifying behavior, and tight integration with CMake and CI systems. Writing good tests with GTest is essential for high-performance C++ development.

In high-performance C++ development, optimization and correctness must go hand in hand. Before you can make code faster, you need to know it is correct. And after you make it faster, you need to verify it is still correct. Google Test (GTest) is the framework that makes this systematic. It provides an expressive, structured way to write automated tests that catch regressions, document expected behavior, and give you the confidence to change code safely.


What Is Google Test?

Google Test is a C++ unit testing framework developed by Google and open-sourced as part of the Google C++ Testing Framework project. It is included in the googletest repository at https://github.com/google/googletest.

Key features:

  • Simple test case and test suite definition macros
  • Rich assertion library covering equality, comparisons, exceptions, and more
  • Test discovery without manual registration
  • Integration with CMake via FetchContent or find_package
  • Colorized output and detailed failure messages
  • Support for test fixtures (setup/teardown)
  • Parameterized tests

Setting Up Google Test

With CMake FetchContent

cmake
cmake_minimum_required(VERSION 3.14)
project(MyProject CXX)

include(FetchContent)
FetchContent_Declare(
    googletest
    URL https://github.com/google/googletest/archive/v1.14.0.zip)
FetchContent_MakeAvailable(googletest)

# Your library
add_library(my_lib STATIC src/mylib.cpp)

# Test executable
add_executable(my_tests tests/test_mylib.cpp)
target_link_libraries(my_tests PRIVATE my_lib GTest::gtest_main)

enable_testing()
include(GoogleTest)
gtest_discover_tests(my_tests)

Including the Header

cpp
#include <gtest/gtest.h>


Writing Your First Test

A Google Test program requires a main function that initializes and runs the tests:

cpp
#include <gtest/gtest.h>

// The function under test
bool IsEven(int num) {
    return num % 2 == 0;
}

// A test case inside the "TestingCode" test suite
TEST(TestingCode, IsEvenTest) {
    ASSERT_TRUE(IsEven(4));    // 4 is even — passes
    ASSERT_FALSE(IsEven(7));   // 7 is odd — passes
    ASSERT_TRUE(IsEven(5));    // 5 is odd — FAILS
}

int main(int argc, char* argv[]) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

When GTest::gtest_main is linked, the main function is provided automatically and you can omit it:

cpp
#include <gtest/gtest.h>

TEST(TestingCode, IsEvenTest) {
    EXPECT_TRUE(IsEven(4));
    EXPECT_FALSE(IsEven(7));
}
// No main() needed when linked against gtest_main


Understanding the Test Macro

cpp
TEST(TestSuiteName, TestName) {
    // assertions
}

  • TestSuiteName: Groups related tests (like a class name for the thing being tested)
  • TestName: Describes what this specific test verifies

Tests are automatically discovered and run. No registration is needed.


Assertions

GTest provides two families of assertion macros:

`ASSERT_*` — Fatal Assertions

If an ASSERT_* fails, the current test function exits immediately. Use when subsequent steps depend on the previous assertion succeeding:

cpp
ASSERT_EQ(expected, actual);    // expected == actual
ASSERT_NE(val1, val2);          // val1 != val2
ASSERT_LT(val1, val2);          // val1 < val2
ASSERT_LE(val1, val2);          // val1 <= val2
ASSERT_GT(val1, val2);          // val1 > val2
ASSERT_GE(val1, val2);          // val1 >= val2
ASSERT_TRUE(condition);         // condition is true
ASSERT_FALSE(condition);        // condition is false
ASSERT_STREQ(str1, str2);       // C-strings are equal
ASSERT_STRNE(str1, str2);       // C-strings are different

`EXPECT_*` — Non-Fatal Assertions

If an EXPECT_* fails, the test continues running. Use to check multiple independent conditions in a single test:

cpp
EXPECT_EQ(expected, actual);
EXPECT_NE(val1, val2);
EXPECT_LT(val1, val2);
EXPECT_TRUE(condition);
EXPECT_FALSE(condition);

Which to Use

text
ASSERT_* vs EXPECT_*:
+------------+--------------------------------------------------+
| ASSERT_*   | Use when test cannot continue if this fails.    |
|            | e.g., pointer is null — can't dereference it    |
+------------+--------------------------------------------------+
| EXPECT_*   | Use when you want all failures reported.         |
|            | e.g., checking multiple fields of a struct      |
+------------+--------------------------------------------------+


Practical Test Examples

Testing a Calculator Function

cpp
#include <gtest/gtest.h>

class Calculator {
public:
    int add(int a, int b)      { return a + b; }
    int subtract(int a, int b) { return a - b; }
    int multiply(int a, int b) { return a * b; }

    double divide(double a, double b) {
        if (b == 0.0) throw std::invalid_argument("division by zero");
        return a / b;
    }
};

TEST(CalculatorTest, AddPositiveNumbers) {
    Calculator calc;
    EXPECT_EQ(calc.add(3, 4), 7);
    EXPECT_EQ(calc.add(0, 0), 0);
    EXPECT_EQ(calc.add(-1, 1), 0);
}

TEST(CalculatorTest, SubtractNumbers) {
    Calculator calc;
    EXPECT_EQ(calc.subtract(10, 4), 6);
    EXPECT_EQ(calc.subtract(0, 5), -5);
}

TEST(CalculatorTest, MultiplyNumbers) {
    Calculator calc;
    EXPECT_EQ(calc.multiply(3, 4), 12);
    EXPECT_EQ(calc.multiply(-2, 5), -10);
}

TEST(CalculatorTest, DivideNumbers) {
    Calculator calc;
    EXPECT_DOUBLE_EQ(calc.divide(10.0, 4.0), 2.5);
}

TEST(CalculatorTest, DivideByZeroThrows) {
    Calculator calc;
    EXPECT_THROW(calc.divide(10.0, 0.0), std::invalid_argument);
}

Testing with Floating Point

Floating-point comparisons require care:

cpp
// Bad: floating-point equality is unreliable
EXPECT_EQ(0.1 + 0.2, 0.3); // likely FAILS due to rounding

// Good: use EXPECT_DOUBLE_EQ (checks within 4 ULPs)
EXPECT_DOUBLE_EQ(0.1 + 0.2, 0.3);

// Or use EXPECT_NEAR for a custom tolerance
EXPECT_NEAR(0.1 + 0.2, 0.3, 1e-9);


Test Fixtures

A test fixture allows you to share setup and teardown code across multiple tests:

cpp
#include <gtest/gtest.h>

class DatabaseTest : public ::testing::Test {
protected:
    // Called before each test
    void SetUp() override {
        db.connect("localhost");
        db.insert({"id": 1, "name": "Alice"});
    }

    // Called after each test
    void TearDown() override {
        db.clear();
        db.disconnect();
    }

    Database db; // shared resource
};

// Tests use the fixture
TEST_F(DatabaseTest, InsertRecord) {
    EXPECT_EQ(db.count(), 1);
}

TEST_F(DatabaseTest, FindRecord) {
    auto record = db.find(1);
    ASSERT_TRUE(record.has_value());
    EXPECT_EQ(record->name, "Alice");
}

Each test gets a fresh instance of the fixture — SetUp and TearDown are called for every test independently.


Parameterized Tests

When you want to run the same test with multiple input values:

cpp
class PrimeTest : public ::testing::TestWithParam<int> {};

INSTANTIATE_TEST_SUITE_P(PrimeValues, PrimeTest,
    ::testing::Values(2, 3, 5, 7, 11, 13));

TEST_P(PrimeTest, IsPrime) {
    EXPECT_TRUE(IsPrime(GetParam()));
}


Debugging GTest Tests

A useful technique for debugging failing tests is printing to stderr:

cpp
TEST(MyTest, DebugExample) {
    // Print debug information from inside a test
    std::cerr << "Request: " << request.to_string() << std::endl;
    std::cerr << "Response: " << response.to_string() << std::endl;

    EXPECT_EQ(response.status, 200);
}

You can also use GTest's RecordProperty to attach metadata to test results:

cpp
TEST(MyTest, WithProperty) {
    RecordProperty("input_size", 1000);
    // ... test code ...
}


Running Tests

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

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

# Run a specific test
./my_tests --gtest_filter=CalculatorTest.AddPositiveNumbers

# Run tests matching a pattern
./my_tests --gtest_filter="Calculator*"

# List all available tests
./my_tests --gtest_list_tests

# Repeat tests to find flaky failures
./my_tests --gtest_repeat=100


Best Practices

text
Google Test Best Practices:
1. One logical concept per test — test one thing at a time
2. Use descriptive test names: MethodName_StateUnderTest_ExpectedBehavior
3. Prefer EXPECT_* over ASSERT_* unless continuation is meaningless
4. Use fixtures for shared setup, not global variables
5. Test edge cases: empty input, zero, negative, overflow
6. Tests should be independent and order-independent
7. Keep tests fast — slow tests don't get run
8. Test the public API, not internal implementation details


Conclusion

Google Test is the standard C++ unit testing framework for good reason. Its macros are expressive and self-documenting, its assertion messages are detailed and actionable, and its integration with CMake and CI systems is seamless. Combined with a discipline of testing before optimizing, GTest is the foundation of high-performance C++ development.

Write the tests first. Then optimize. Then verify the tests still pass. This is the workflow that produces both fast and correct software.