Skip to main content

GTest Basics

Google Test (GTest) is Google's C++ testing framework. Tests are plain functions wrapped in macros; GTest collects them automatically and reports failures with file name, line number, and the expected vs. actual values.

Setup

CMake (FetchContent)

The recommended approach: CMake downloads GTest at configure time, so there is no manual install. gtest_discover_tests registers each TEST()/TEST_F() as a separate CTest target, enabling per-test filtering and parallel runs.

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

add_executable(my_tests test_foo.cpp)
target_link_libraries(my_tests GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(my_tests)

Package manager

# vcpkg
vcpkg install gtest

# apt
sudo apt install libgtest-dev

Test Structure

A test is declared with TEST(SuiteName, TestName). Suite groups related tests; name describes what this specific case verifies. GTest discovers all tests at link time — no registration needed. Linking against GTest::gtest_main provides a main() that initializes GTest and runs all tests, so you only need to write one if you want custom initialization logic.

#include <gtest/gtest.h>

TEST(MathTest, AddsTwoIntegers) {
EXPECT_EQ(2 + 2, 4);
}

TEST(MathTest, DividesCorrectly) {
EXPECT_DOUBLE_EQ(10.0 / 3.0, 10.0 / 3.0);
}

// Only needed if you want custom init before RUN_ALL_TESTS
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

Assertions

GTest provides two flavors for every assertion:

  • EXPECT_* — records failure and continues the test. Use when a failed check doesn't invalidate the rest of the test.
  • ASSERT_* — records failure and aborts the current test immediately. Use when a failed check would make subsequent code crash or produce meaningless results (e.g., a null pointer dereference).

Equality / Comparison

These work with any type that has the corresponding operator defined.

EXPECT_EQ(a, b); // a == b
EXPECT_NE(a, b); // a != b
EXPECT_LT(a, b); // a < b
EXPECT_LE(a, b); // a <= b
EXPECT_GT(a, b); // a > b
EXPECT_GE(a, b); // a >= b

Boolean

EXPECT_TRUE(condition);
EXPECT_FALSE(condition);

Floating point

Never use EXPECT_EQ for floats — rounding makes exact equality unreliable. GTest provides ULP-based comparison (4 ULPs by default) and an absolute-error variant.

EXPECT_FLOAT_EQ(a, b); // within 4 ULPs (float precision)
EXPECT_DOUBLE_EQ(a, b); // within 4 ULPs (double precision)
EXPECT_NEAR(a, b, abs_err); // |a - b| <= abs_err (explicit tolerance)

Strings

For C-style strings (char*), use the STR variants — they compare content, not pointer addresses. For std::string, EXPECT_EQ works fine.

EXPECT_STREQ(c_str1, c_str2); // strcmp == 0
EXPECT_STRNE(c_str1, c_str2);
EXPECT_STRCASEEQ(c_str1, c_str2); // case-insensitive
// For std::string just use EXPECT_EQ

Exceptions

These test that code throws (or doesn't). The expression is executed inside the macro; the test doesn't crash if an exception is thrown.

EXPECT_THROW(expr, ExceptionType); // throws exactly ExceptionType (or subclass)
EXPECT_NO_THROW(expr); // must not throw anything
EXPECT_ANY_THROW(expr); // must throw something

Death tests

Death tests verify that code terminates the process — useful for testing assert(), std::abort(), or intentional exit() calls. GTest forks a child process, runs the expression there, and checks the outcome.

EXPECT_DEATH(expr, regex); // process crashes, stderr matches regex
EXPECT_EXIT(expr, pred, regex); // process exits, exit code matches predicate

Custom failure message

Any assertion accepts a << chain to attach context shown only on failure. Useful when the assertion values alone don't explain what went wrong.

EXPECT_EQ(result, expected) << "Input was: " << input;

Test Fixtures

When multiple tests need the same objects, constructing them in every TEST() body is repetitive and error-prone. A fixture centralizes setup and teardown in a class. SetUp() runs before each test, TearDown() after — each test gets a fresh instance, so tests cannot affect each other through shared state.

class VectorTest : public ::testing::Test {
protected:
void SetUp() override {
v = {1, 2, 3};
}
void TearDown() override {
// optional — destructor also works for cleanup
}

std::vector<int> v;
};

// TEST_F takes the fixture class as the first argument instead of a suite name
TEST_F(VectorTest, SizeIsThree) {
EXPECT_EQ(v.size(), 3u);
}

TEST_F(VectorTest, FirstElementIsOne) {
EXPECT_EQ(v[0], 1);
}

Parameterized Tests

Parameterized tests run the same test body against a set of inputs, eliminating copy-pasted test cases that differ only in values. Inherit from ::testing::TestWithParam<T>, retrieve the current value with GetParam(), then instantiate with INSTANTIATE_TEST_SUITE_P.

class EvenTest : public ::testing::TestWithParam<int> {};

TEST_P(EvenTest, IsEven) {
EXPECT_EQ(GetParam() % 2, 0);
}

INSTANTIATE_TEST_SUITE_P(
EvenValues, // instance name (prefix in test output)
EvenTest,
::testing::Values(2, 4, 6, 100)
);

Typed tests run against multiple types rather than multiple values — useful for generic containers or algorithms that should work for int, float, std::string, etc.

using MyTypes = ::testing::Types<int, long, float>;
TYPED_TEST_SUITE(FooTest, MyTypes);

TYPED_TEST(FooTest, IsZeroInitialized) {
TypeParam val{};
EXPECT_EQ(val, TypeParam{0});
}

Running Tests

The test binary accepts GTest flags directly. No CTest required for development — just run the binary.

./my_tests # run all tests
./my_tests --gtest_filter="Suite.*" # run all tests in Suite
./my_tests --gtest_filter="-*Death*" # exclude tests matching *Death*
./my_tests --gtest_repeat=10 # repeat all tests 10 times (catch flakiness)
./my_tests --gtest_shuffle # randomize order (catch order dependencies)
./my_tests --gtest_output=xml:report.xml # machine-readable output for CI

Filter syntax: SuiteName.TestName, wildcards *, exclude with - prefix, separate multiple patterns with :.

Disabling Tests

Prefix a suite or test name with DISABLED_ to skip it without deleting the code. Disabled tests still compile (so they don't rot silently) but are excluded from runs and listed separately in the output.

TEST(DISABLED_FlakyTest, SometimesFails) { ... }
TEST_F(MyFixture, DISABLED_NotImplementedYet) { ... }