Skip to content
C++
Analysis & Formatting
Updated 2026-05-01T00:00:00.000Z

GoogleTest

Google's C++ testing framework — xUnit-style tests, rich matchers, mocking with gMock, and seamless CMake/CTest integration.

TL;DR

GoogleTest (gtest) is the most widely used C++ unit testing framework. It provides xUnit-style TEST() macros, a rich matcher library, and gMock for mocking. Add it via CMake FetchContent or vcpkg and you're writing tests in minutes.

cpp
#include <gtest/gtest.h>

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

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

Setup

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

enable_testing()

add_executable(myapp_tests tests/math_test.cpp)
target_link_libraries(myapp_tests GTest::gtest_main GTest::gmock)
include(GoogleTest)
gtest_discover_tests(myapp_tests)

vcpkg

bash
vcpkg install gtest
cmake
find_package(GTest CONFIG REQUIRED)
target_link_libraries(myapp_tests GTest::gtest_main GTest::gmock)

Test macros

cpp
// Basic assertions — non-fatal (test continues)
EXPECT_EQ(actual, expected);    // a == b
EXPECT_NE(actual, expected);    // a != b
EXPECT_LT(actual, expected);    // a < b
EXPECT_LE(actual, expected);    // a <= b
EXPECT_GT(actual, expected);    // a > b
EXPECT_GE(actual, expected);    // a >= b
EXPECT_TRUE(condition);
EXPECT_FALSE(condition);

// Fatal assertions — test stops on failure
ASSERT_EQ(actual, expected);
ASSERT_TRUE(condition);
// ... (ASSERT_ variants for all EXPECT_ macros)

// String matchers
EXPECT_STREQ("hello", str.c_str());
EXPECT_STRCASEEQ("Hello", str.c_str());  // case-insensitive

// Floating point
EXPECT_FLOAT_EQ(3.14f, result);          // ~4 ULP tolerance
EXPECT_DOUBLE_EQ(3.14, result);
EXPECT_NEAR(3.14, result, 0.001);        // custom tolerance

Test fixtures

cpp
class DatabaseTest : public ::testing::Test {
protected:
    void SetUp() override {
        db_.connect("test_db");
        db_.clear();
    }

    void TearDown() override {
        db_.disconnect();
    }

    Database db_;
};

TEST_F(DatabaseTest, InsertAndRetrieve) {
    db_.insert("key", "value");
    EXPECT_EQ(db_.get("key"), "value");
}

TEST_F(DatabaseTest, ReturnNullForMissing) {
    EXPECT_EQ(db_.get("missing"), std::nullopt);
}

SetUp runs before each test; TearDown after each. The fixture object is recreated for every test — no shared state.

Parameterized tests

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

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

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

For multiple parameter types:

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

TYPED_TEST(TypedMathTest, Zero) {
    TypeParam x = 0;
    EXPECT_EQ(x + x, x);
}

gMock — mocking

cpp
#include <gmock/gmock.h>

class MockFileSystem : public FileSystem {
public:
    MOCK_METHOD(bool, exists, (const std::string& path), (override));
    MOCK_METHOD(std::string, read, (const std::string& path), (override));
};

TEST(BackupTest, SkipsNonExistentFiles) {
    MockFileSystem fs;
    EXPECT_CALL(fs, exists("/data/file.txt")).WillOnce(::testing::Return(false));
    EXPECT_CALL(fs, read(::testing::_)).Times(0);  // never called

    BackupService svc(fs);
    svc.backup("/data/file.txt");
}

Common matchers

cpp
using namespace ::testing;

EXPECT_CALL(mock, method(Eq(42)));          // exact value
EXPECT_CALL(mock, method(Gt(10)));          // > 10
EXPECT_CALL(mock, method(HasSubstr("foo"))); // string contains
EXPECT_CALL(mock, method(_));               // any argument
EXPECT_CALL(mock, method(An<int>()));       // any int

// Return values
.WillOnce(Return(42));
.WillRepeatedly(Return(0));
.WillOnce(Throw(std::runtime_error("fail")));
.WillOnce(Invoke([](int x) { return x * 2; }));

// Call count
.Times(0);           // never called
.Times(1);           // exactly once
.Times(AtLeast(2));  // at least twice
.Times(Between(1,3));

Exception testing

cpp
TEST(ParserTest, ThrowsOnInvalidInput) {
    EXPECT_THROW(parse("!!!"), std::invalid_argument);
    EXPECT_NO_THROW(parse("valid"));
}

// Check exception message
TEST(ParserTest, ExceptionMessage) {
    try {
        parse("!!!");
        FAIL() << "Expected exception";
    } catch (const std::invalid_argument& e) {
        EXPECT_THAT(e.what(), HasSubstr("invalid"));
    }
}

Running tests

bash
# Run all tests
./myapp_tests

# Run specific test
./myapp_tests --gtest_filter=DatabaseTest.InsertAndRetrieve

# Run tests matching pattern
./myapp_tests --gtest_filter=*Prime*

# Repeat flaky tests
./myapp_tests --gtest_repeat=100 --gtest_filter=FlakyTest*

# Output XML for CI
./myapp_tests --gtest_output=xml:results.xml

# Via CTest
ctest --test-dir build -V

Best practices

Name tests clearly: TEST(ClassName, MethodName_Scenario_ExpectedResult) — e.g., TEST(Stack, Push_OnEmptyStack_SizeIsOne).

One assertion per test: Prefer multiple small tests over one large test with many assertions. A failing ASSERT stops the test, hiding other failures.

Test behavior, not implementation: Test via the public interface. Don't test private methods directly.

Keep tests fast: Mock external dependencies (DB, network, filesystem). Unit tests should run in milliseconds.

Use SCOPED_TRACE for parameterized failure messages:

cpp
for (auto& input : test_cases) {
    SCOPED_TRACE("Input: " + input.name);
    EXPECT_EQ(compute(input.value), input.expected);
}

Test Fixtures (TEST_F) in Depth

Fixtures are the primary tool for sharing setup logic across related tests. Every TEST_F in a suite gets a fresh fixture object — SetUp runs before the test body, TearDown runs after it, even when the test fails.

cpp
#include <gtest/gtest.h>
#include "http_client.h"

class HttpClientTest : public ::testing::Test {
protected:
    // Called before EACH test in this suite.
    void SetUp() override {
        client_ = std::make_unique<HttpClient>();
        client_->setBaseUrl("http://localhost:8080");
        client_->setTimeout(std::chrono::seconds(5));
    }

    // Called after EACH test, even if it fails or throws.
    void TearDown() override {
        client_->disconnect();
        // No manual delete — unique_ptr cleans up automatically.
    }

    // Helpers visible to all TEST_F bodies in this suite.
    HttpResponse get(const std::string& path) {
        return client_->get(path);
    }

    std::unique_ptr<HttpClient> client_;
};

TEST_F(HttpClientTest, ReturnsOkForHealthEndpoint) {
    auto resp = get("/health");
    EXPECT_EQ(resp.statusCode, 200);
}

TEST_F(HttpClientTest, Returns404ForUnknownPath) {
    auto resp = get("/no-such-route");
    EXPECT_EQ(resp.statusCode, 404);
}

RAII in Fixtures

Prefer RAII members over explicit TearDown. If SetUp can throw, any members already constructed will be destroyed automatically — but TearDown is not called when SetUp throws. Wrap resources in smart pointers or RAII guards so cleanup is guaranteed regardless.

cpp
class TempDirTest : public ::testing::Test {
protected:
    void SetUp() override {
        // TempDir constructor creates the directory; destructor removes it.
        // If a later SetUp line throws, TempDir's destructor still fires.
        tmpDir_ = std::make_unique<TempDir>("/tmp/gtest_XXXXXX");
    }

    // No TearDown needed — TempDir RAII handles removal.

    std::unique_ptr<TempDir> tmpDir_;
};

Suite-Level Setup with SetUpTestSuite

Use SetUpTestSuite / TearDownTestSuite for expensive one-time state (e.g., starting a server, loading a large data file). These are static methods called once per suite rather than once per test.

cpp
class ServerTest : public ::testing::Test {
public:
    static void SetUpTestSuite() {
        server_ = std::make_unique<TestServer>();
        server_->start(9000);
    }

    static void TearDownTestSuite() {
        server_->stop();
        server_.reset();
    }

protected:
    static std::unique_ptr<TestServer> server_;
};

// Definition in .cpp
std::unique_ptr<TestServer> ServerTest::server_;

TEST_F(ServerTest, AcceptsConnections) {
    EXPECT_TRUE(server_->isRunning());
}

Parameterized Tests in Depth

Parameterized tests eliminate copy-paste by running the same logic over many inputs. TEST_P pairs with INSTANTIATE_TEST_SUITE_P to generate a named test case for every value.

cpp
#include <gtest/gtest.h>

struct DivCase { int num, den, expected; };

class DivisionTest : public ::testing::TestWithParam<DivCase> {};

TEST_P(DivisionTest, ComputesQuotient) {
    auto [num, den, expected] = GetParam();
    EXPECT_EQ(num / den, expected);
}

INSTANTIATE_TEST_SUITE_P(
    BasicDivision,
    DivisionTest,
    ::testing::Values(
        DivCase{10, 2, 5},
        DivCase{9,  3, 3},
        DivCase{7,  1, 7}
    )
);

ValuesIn — Driving Tests from a Container

cpp
const std::vector<std::string> kValidUrls = {
    "https://example.com",
    "http://foo.bar/path?q=1",
};

class UrlParseTest : public ::testing::TestWithParam<std::string> {};

TEST_P(UrlParseTest, ParsesWithoutThrowing) {
    EXPECT_NO_THROW(Url::parse(GetParam()));
}

INSTANTIATE_TEST_SUITE_P(ValidUrls, UrlParseTest,
    ::testing::ValuesIn(kValidUrls));

Combine — Cartesian Product of Parameter Sets

testing::Combine generates the full Cartesian product — useful for testing every combination of two independent axes.

cpp
class EncodingTest
    : public ::testing::TestWithParam<std::tuple<Encoding, CompressionLevel>> {};

TEST_P(EncodingTest, RoundTrips) {
    auto [enc, level] = GetParam();
    auto data = makeTestPayload();
    EXPECT_EQ(decode(encode(data, enc, level), enc), data);
}

INSTANTIATE_TEST_SUITE_P(
    All,
    EncodingTest,
    ::testing::Combine(
        ::testing::Values(Encoding::UTF8, Encoding::UTF16, Encoding::Latin1),
        ::testing::Values(CompressionLevel::None, CompressionLevel::Fast,
                          CompressionLevel::Best)
    )
);

Typed Tests — Same Logic Across Multiple Types

TYPED_TEST runs identical assertions against every type in a list, which is ideal for testing generic containers or numeric algorithms.

cpp
template <typename T>
class StackTest : public ::testing::Test {
protected:
    Stack<T> stack_;
};

using StackTypes = ::testing::Types<int, double, std::string>;
TYPED_TEST_SUITE(StackTest, StackTypes);

TYPED_TEST(StackTest, IsEmptyOnConstruction) {
    EXPECT_TRUE(this->stack_.empty());
    EXPECT_EQ(this->stack_.size(), 0u);
}

TYPED_TEST(StackTest, PushIncreasesSize) {
    this->stack_.push(TypeParam{});
    EXPECT_EQ(this->stack_.size(), 1u);
}

GMock — Mocking in Depth

GMock lets you replace real dependencies with controlled fakes without writing boilerplate stub classes by hand. Declare mock methods with MOCK_METHOD, set expectations with EXPECT_CALL, and provide default behavior with ON_CALL.

cpp
#include <gmock/gmock.h>

// Interface under test
class Logger {
public:
    virtual ~Logger() = default;
    virtual void log(int level, std::string_view message) = 0;
    virtual bool isEnabled(int level) const = 0;
};

// Generated mock
class MockLogger : public Logger {
public:
    MOCK_METHOD(void, log, (int level, std::string_view message), (override));
    MOCK_METHOD(bool, isEnabled, (int level), (const, override));
};

EXPECT_CALL and ON_CALL

cpp
using namespace ::testing;

TEST(ServiceTest, LogsErrorOnFailure) {
    MockLogger logger;

    // ON_CALL: default behavior for all calls (no expectation failure if not called)
    ON_CALL(logger, isEnabled(_)).WillByDefault(Return(true));

    // EXPECT_CALL: expectation + behavior; failure if not met
    EXPECT_CALL(logger, log(3, HasSubstr("timeout")))
        .Times(1);

    Service svc(logger);
    svc.run();  // Should internally call logger.log(3, "connection timeout")
}

InSequence — Enforcing Call Order

cpp
TEST(ProtocolTest, SendsHandshakeBeforeData) {
    MockTransport transport;
    InSequence seq;  // All EXPECT_CALLs below must fire in order.

    EXPECT_CALL(transport, send("HELLO")).Times(1);
    EXPECT_CALL(transport, send("DATA:foo")).Times(1);
    EXPECT_CALL(transport, send("BYE")).Times(1);

    Protocol proto(transport);
    proto.sendMessage("foo");
}

SaveArg — Capturing Arguments

SaveArg<N> captures the Nth argument of a mock call for later inspection.

cpp
TEST(QueueTest, PassesCorrectJobToWorker) {
    MockWorker worker;
    Job capturedJob;

    EXPECT_CALL(worker, execute(_))
        .WillOnce(SaveArg<0>(&capturedJob));

    TaskQueue queue(worker);
    queue.push(Job{"render", 42});

    EXPECT_EQ(capturedJob.name, "render");
    EXPECT_EQ(capturedJob.priority, 42);
}

Cardinality Shorthands

cpp
EXPECT_CALL(mock, method(_)).Times(AnyNumber());   // zero or more
EXPECT_CALL(mock, method(_)).Times(AtLeast(1));    // one or more
EXPECT_CALL(mock, method(_)).Times(AtMost(3));     // up to three
EXPECT_CALL(mock, method(_)).Times(Between(2, 5)); // inclusive range
EXPECT_CALL(mock, method(_)).Times(Exactly(7));    // exactly seven

Matchers

The matcher library turns assertion failures into readable diagnostics — instead of EXPECT_TRUE(Contains(...)), you write EXPECT_THAT(container, Contains(5)) and gtest prints what it expected and what it saw.

Container Matchers

cpp
#include <gmock/gmock.h>
using namespace ::testing;

TEST(VectorTest, ContainsExpectedElements) {
    std::vector<int> v = {1, 2, 3, 4, 5};

    EXPECT_THAT(v, Contains(3));
    EXPECT_THAT(v, Not(Contains(99)));
    EXPECT_THAT(v, ElementsAre(1, 2, 3, 4, 5));       // exact order
    EXPECT_THAT(v, UnorderedElementsAre(5, 3, 1, 4, 2)); // any order
    EXPECT_THAT(v, Each(Gt(0)));                        // every element > 0
    EXPECT_THAT(v, SizeIs(5));
    EXPECT_THAT(v, IsEmpty());                          // or Not(IsEmpty())
}

String Matchers

cpp
TEST(StringTest, ChecksSubstrings) {
    std::string msg = "Error: file not found";

    EXPECT_THAT(msg, HasSubstr("not found"));
    EXPECT_THAT(msg, StartsWith("Error"));
    EXPECT_THAT(msg, EndsWith("found"));
    EXPECT_THAT(msg, MatchesRegex("Error:.*not found"));
    EXPECT_THAT(msg, ContainsRegex("file"));
}

Combining Matchers

cpp
TEST(ResponseTest, IsSuccessfulJson) {
    Response r = fetchData();

    EXPECT_THAT(r.statusCode, AllOf(Ge(200), Lt(300)));
    EXPECT_THAT(r.contentType, AnyOf(HasSubstr("json"), HasSubstr("text")));
    EXPECT_THAT(r.body, Not(IsEmpty()));
}

Pointer and Optional Matchers

cpp
TEST(NodeTest, PointeeHasCorrectValue) {
    auto p = std::make_unique<int>(42);
    EXPECT_THAT(p.get(), Pointee(42));

    std::optional<std::string> opt = "hello";
    EXPECT_THAT(opt, Optional(HasSubstr("ell")));
}

Custom Matchers with MATCHER

Define reusable matchers for your own types with MATCHER or MATCHER_P.

cpp
// Zero-argument custom matcher
MATCHER(IsEven, "is even") {
    return (arg % 2) == 0;
}

// One-parameter custom matcher
MATCHER_P(IsDivisibleBy, n, "") {
    return (arg % n) == 0;
}

TEST(CustomMatcherTest, Works) {
    EXPECT_THAT(4, IsEven());
    EXPECT_THAT(15, IsDivisibleBy(5));
    EXPECT_THAT(std::vector<int>{2, 4, 6}, Each(IsEven()));
}

Death Tests

Death tests verify that code terminates the process — catching calls to std::abort, std::terminate, or explicit exit() on invalid inputs. They are essential for testing precondition enforcement in low-level code.

cpp
#include <gtest/gtest.h>

void mustBePositive(int x) {
    if (x <= 0) std::abort();
}

TEST(DeathTest, AbortsOnNonPositive) {
    EXPECT_DEATH(mustBePositive(-1), "");   // any output (or none)
    EXPECT_DEATH(mustBePositive(0),  "");
}

The second argument to EXPECT_DEATH is a regex matched against stderr output. Use it to distinguish which abort fired:

cpp
void checkBounds(int idx, int size) {
    if (idx < 0 || idx >= size) {
        std::fprintf(stderr, "index %d out of bounds [0, %d)\n", idx, size);
        std::abort();
    }
}

TEST(BoundsTest, AbortMessageContainsIndex) {
    EXPECT_DEATH(checkBounds(10, 5), "10 out of bounds");
}

ASSERT_DEATH and EXPECT_EXIT

cpp
// Fatal variant — stops the test on failure (like ASSERT_ vs EXPECT_)
ASSERT_DEATH(badCall(), "expected pattern");

// EXPECT_EXIT — checks exit code and stderr
EXPECT_EXIT(
    std::exit(42),
    ::testing::ExitedWithCode(42),
    ""
);

// Testing abnormal termination (signal-based on POSIX)
EXPECT_EXIT(
    std::abort(),
    ::testing::KilledBySignal(SIGABRT),
    ""
);

Death Test Style

GoogleTest runs death tests in a child process by default ("fast" style forks before test setup; "threadsafe" re-executes the entire binary). Set the style globally or per-test:

cpp
TEST(MyTest, AbortTest) {
    ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    EXPECT_DEATH(dangerousFunction(), "");
}

Prefer "threadsafe" when the code under test spawns threads; use "fast" (the default) everywhere else to keep the test suite quick.

Test Output and Filtering

The --gtest_filter flag is the fastest way to run a subset of tests during development. Patterns support * wildcards and - exclusions.

bash
# Run exactly one test
./myapp_tests --gtest_filter=DatabaseTest.InsertAndRetrieve

# Run all tests in a suite
./myapp_tests --gtest_filter=DatabaseTest.*

# Run tests matching a substring
./myapp_tests --gtest_filter=*Insert*

# Exclude tests matching a pattern
./myapp_tests --gtest_filter=-*Slow*

# Combine inclusions and exclusions (colon separates multiple patterns)
./myapp_tests --gtest_filter=DatabaseTest.*:CacheTest.*-*Flaky*

Discovery and Repetition

bash
# Print every test name without running them
./myapp_tests --gtest_list_tests

# Run each test 20 times (useful for catching flakiness)
./myapp_tests --gtest_repeat=20

# Randomize test order (catches hidden inter-test dependencies)
./myapp_tests --gtest_shuffle

# Combine: shuffle and repeat
./myapp_tests --gtest_shuffle --gtest_repeat=50 --gtest_filter=*Flaky*

CI Output

bash
# Emit JUnit-compatible XML (consumed by Jenkins, GitLab, GitHub Actions)
./myapp_tests --gtest_output=xml:build/reports/gtest.xml

# Emit JSON report
./myapp_tests --gtest_output=json:build/reports/gtest.json

# Print all test names as they run (verbose)
./myapp_tests --gtest_print_time=1 --gtest_color=yes

GitHub Actions example consuming the XML report:

yaml
- name: Run tests
  run: ./build/myapp_tests --gtest_output=xml:report.xml

- name: Publish test results
  uses: dorny/test-reporter@v1
  with:
    name: GoogleTest Results
    path: report.xml
    reporter: java-junit

CMake Integration in Depth

gtest_discover_tests() is the preferred CMake helper — it runs the test binary with --gtest_list_tests at build time and registers each test case individually with CTest, so ctest -R MyTest works without recompiling.

Full FetchContent Setup

cmake
cmake_minimum_required(VERSION 3.21)
project(MyProject CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip
  URL_HASH SHA256=7b42b4d6ed48810cfaa5cd79f3c20c861a5c8e4d5b3e5d6e2f3f5e0e3e3c3d2
  DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)

# Prevent installing gtest alongside your project
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
# On Windows: prevent overriding CRT linkage
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(myapp_tests
    tests/math_test.cpp
    tests/parser_test.cpp
    tests/db_test.cpp
)
target_link_libraries(myapp_tests
    PRIVATE
        myapp_lib
        GTest::gtest_main
        GTest::gmock
)
target_include_directories(myapp_tests PRIVATE ${CMAKE_SOURCE_DIR}/src)

include(GoogleTest)
gtest_discover_tests(myapp_tests
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    PROPERTIES TIMEOUT 30          # per-test timeout in seconds
)

gtest_discover_tests vs gtest_add_tests

Featuregtest_discover_testsgtest_add_tests
Discovery methodRuns binary at build timeParses source files
AccuracyExact — handles dynamic test namesMisses TEST_P, macros
Requires buildYes (binary must be linkable)No
RecommendedYes (default choice)Legacy / cross-compilation only

Running via CTest

bash
# Build and run all tests
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
ctest --test-dir build

# Only show output for failing tests (CI-friendly)
ctest --test-dir build --output-on-failure

# Run tests in parallel
ctest --test-dir build -j8

# Filter by name
ctest --test-dir build -R "DatabaseTest"

# Verbose output
ctest --test-dir build -V

# Emit JUnit XML (CTest native)
ctest --test-dir build --output-junit build/ctest-report.xml

Preset Integration (CMakePresets.json)

CMake presets keep CI and developer workflows consistent without passing flags manually.

json
{
  "version": 6,
  "configurePresets": [
    {
      "name": "debug",
      "binaryDir": "${sourceDir}/build/debug",
      "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }
    }
  ],
  "buildPresets": [
    { "name": "debug", "configurePreset": "debug" }
  ],
  "testPresets": [
    {
      "name": "debug",
      "configurePreset": "debug",
      "output": { "outputOnFailure": true },
      "execution": { "jobs": 8, "timeout": 60 }
    }
  ]
}
bash
cmake --preset debug
cmake --build --preset debug
ctest --preset debug
Edit on GitHubUpdated 2026-05-01T00:00:00.000Z