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
FetchContentorfind_package - Colorized output and detailed failure messages
- Support for test fixtures (setup/teardown)
- Parameterized tests
Setting Up Google Test
With CMake FetchContent
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
#include <gtest/gtest.h>
Writing Your First Test
A Google Test program requires a main function that initializes and runs the tests:
#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:
#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
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:
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:
EXPECT_EQ(expected, actual);
EXPECT_NE(val1, val2);
EXPECT_LT(val1, val2);
EXPECT_TRUE(condition);
EXPECT_FALSE(condition);
Which to Use
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
#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:
// 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:
#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:
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:
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:
TEST(MyTest, WithProperty) {
RecordProperty("input_size", 1000);
// ... test code ...
}
Running Tests
# 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
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.