Product
Solutions
Resources
Company
Download Trial Book a Demo

GTest Tutorial: Getting Started with Google Test

From an empty CMakeLists.txt to a passing test suite with fixtures, parameterized tests and mocks. This hands-on tutorial walks you through GoogleTest end to end — then shows how to prove your tests actually exercise the code they claim to.

GoogleTest — often shortened to GTest — is the most widely used C++ unit testing framework in the world. It powers the test suites of Chromium, LLVM, Protocol Buffers and countless embedded and server codebases, and it ships with a companion mocking library, GoogleMock, in the same repository. If you write C++ and you have not yet picked a test framework, GoogleTest is the safe, well-documented default.

This tutorial takes you from nothing to a working suite. You'll add GoogleTest to a project with CMake, write your first TEST, learn the assertion macros, share setup across tests with fixtures, run the same logic over many inputs with parameterized tests, isolate dependencies with GoogleMock, and finally run everything and measure how much of your code the tests really touched. Every snippet is small enough to type out and compiles conceptually as shown.

What is GoogleTest?

GoogleTest is an xUnit-style testing framework for C++. If you've used JUnit in Java or pytest in Python, the model will feel familiar: you write small, independent test functions, the framework discovers and runs them, and it reports pass/fail with rich diagnostics on failure. There is no separate test runner to register your tests with — the macros do that for you.

Practically, GoogleTest is two things:

  • A header you include (<gtest/gtest.h>) that gives you the TEST macros, assertions and fixtures.
  • A library you link (gtest plus gtest_main, which provides a ready-made main()).

Bundled alongside it is GoogleMock (the gmock target and <gmock/gmock.h> header), which lets you generate mock implementations of interfaces and set expectations on how they're called. Because they live in one repository and build together, adding GoogleTest gives you mocking for free.

Installing GoogleTest with CMake

The cleanest way to add GoogleTest to a modern C++ project is CMake's FetchContent module. It downloads a pinned version of GoogleTest at configure time and builds it as part of your tree — no system-wide install, no version drift between machines. Create a CMakeLists.txt like this:

CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(MyApp LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# --- Pull in GoogleTest (and GoogleMock) ---
include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip
)
FetchContent_MakeAvailable(googletest)

enable_testing()

# --- Code under test ---
add_library(calc src/calc.cpp)
target_include_directories(calc PUBLIC include)

# --- Test executable ---
add_executable(calc_tests tests/calc_test.cpp)
target_link_libraries(calc_tests PRIVATE calc GTest::gtest_main GTest::gmock)

include(GoogleTest)
gtest_discover_tests(calc_tests)

Linking GTest::gtest_main means you don't have to write your own main().

Configure and build with the usual two commands. gtest_discover_tests registers every test with CTest so they run individually:

terminal — configure & build
$ cmake -S . -B build
$ cmake --build build

   built target calc
   built target calc_tests

Writing your first test

Let's give ourselves something to test. Suppose calc.h declares a tiny function:

include/calc.h
// Returns the sum of two integers.
int add(int a, int b);

A GoogleTest test is written with the TEST macro, which takes a test suite name and a test name: TEST(SuiteName, TestName). Both are plain identifiers, not strings, and the body is just C++:

tests/calc_test.cpp
#include <gtest/gtest.h>
#include "calc.h"

TEST(AddTest, HandlesPositiveInput) {
  EXPECT_EQ(add(2, 3), 5);
}

TEST(AddTest, HandlesNegativeInput) {
  EXPECT_EQ(add(-4, 1), -3);
}

That's a complete, runnable test file. Each TEST is independent — GoogleTest runs them in isolation and a failure in one never stops the others. When you build and run the executable, you get clear, colorized output telling you which tests ran and passed.

Assertions: ASSERT vs EXPECT

Assertions are how a test states what it expects. GoogleTest gives you two families that differ in one important way:

  • EXPECT_* reports a failure but continues running the rest of the test. Use it for most checks, so a single run surfaces every problem.
  • ASSERT_* reports a failure and aborts the current test immediately. Use it when continuing makes no sense — for example, after checking a pointer is non-null before you dereference it.

Both families share the same comparison vocabulary. The common ones:

tests/assertions_demo.cpp
TEST(Assertions, Vocabulary) {
  EXPECT_EQ(add(2, 2), 4);        // equal
  EXPECT_NE(add(2, 2), 5);        // not equal
  EXPECT_TRUE(add(1, 1) > 0);      // boolean true
  EXPECT_FALSE(add(0, 0) != 0);    // boolean false

  EXPECT_NEAR(3.14159, 3.14, 0.01);  // floats within tolerance
  EXPECT_STREQ("hi", "hi");          // C-string contents equal

  int* p = make_thing();
  ASSERT_NE(p, nullptr);              // stop here if null
  EXPECT_EQ(*p, 42);                  // safe to dereference now
}

Use EXPECT_NEAR for floating-point — never EXPECT_EQ on double, since rounding makes exact equality unreliable. And reach for EXPECT_STREQ when comparing C-style char* strings, because EXPECT_EQ on raw pointers compares addresses, not characters.

The one-line rule of thumb

Reach for EXPECT_* by default — you want every failure in one run. Switch to ASSERT_* only when a failed check would make the rest of the test crash or produce meaningless results (null pointers, empty containers, failed setup).

Test fixtures with TEST_F

When several tests need the same setup — a database handle, a populated object, a temp directory — copying that setup into each TEST gets repetitive and error-prone. A fixture solves this. You write a class deriving from ::testing::Test, put the shared state in it, and GoogleTest creates a fresh instance for every test so they never bleed into one another.

Override SetUp() to run before each test and TearDown() to run after. Then write tests with TEST_F (the F is for fixture), passing the fixture class name as the first argument:

tests/stack_test.cpp
class StackTest : public ::testing::Test {
 protected:
  void SetUp() override {
    stack_.push(1);
    stack_.push(2);     // runs before EVERY test below
  }
  // void TearDown() override { ... }  // optional cleanup

  std::stack<int> stack_;
};

TEST_F(StackTest, StartsWithTwoItems) {
  EXPECT_EQ(stack_.size(), 2u);
}

TEST_F(StackTest, PopReturnsLastPushed) {
  EXPECT_EQ(stack_.top(), 2);
  stack_.pop();
  EXPECT_EQ(stack_.top(), 1);
}

Because each TEST_F gets its own StackTest instance, the pop() in the second test cannot affect the first. That isolation is what makes a test suite trustworthy.

Parameterized tests with TEST_P

Sometimes you want to run the same logic against many inputs without copy-pasting a test for each. Parameterized tests do exactly that. You derive a fixture from ::testing::TestWithParam<T>, read the current value with GetParam(), write the body once with TEST_P, and feed in the values with INSTANTIATE_TEST_SUITE_P:

tests/is_even_test.cpp
// Function under test: bool is_even(int n);

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

TEST_P(IsEvenTest, ReturnsTrueForEvenNumbers) {
  int value = GetParam();
  EXPECT_TRUE(is_even(value));
}

INSTANTIATE_TEST_SUITE_P(
    EvenSamples,                 // instantiation name
    IsEvenTest,                  // the fixture
    ::testing::Values(0, 2, 8, 100, -6));

GoogleTest expands that into one named test instance per value, so a single failure tells you exactly which input broke. Beyond Values, you can use Range, ValuesIn (to read from a container) and Combine (for the cartesian product of several parameter sets) — handy for boundary and table-driven testing.

Mocking with GoogleMock

Real code talks to things that are slow, non-deterministic or unavailable in a test — networks, clocks, hardware. GoogleMock lets you replace such a dependency with a mock that you fully control. Define mock methods with the MOCK_METHOD macro, then state expectations with EXPECT_CALL.

Say your code depends on an abstract Database interface. You mock it, set up what its calls should return, and verify it was used correctly:

tests/user_service_test.cpp
#include <gmock/gmock.h>
using ::testing::Return;
using ::testing::_;

class MockDatabase : public Database {
 public:
  MOCK_METHOD(std::string, getName, (int id), (override));
  MOCK_METHOD(bool, save, (const User& u), (override));
};

TEST(UserServiceTest, GreetsByName) {
  MockDatabase db;
  EXPECT_CALL(db, getName(7))            // expect getName(7)...
      .Times(1)                          // ...exactly once...
      .WillOnce(Return("Ada"));            // ...returning "Ada"

  UserService service(&db);
  EXPECT_EQ(service.greet(7), "Hello, Ada");
}

The _ token is a matcher meaning "any value." GoogleMock has a rich matcher library — Eq, Gt, HasSubstr, ElementsAre and many more — for expressing precisely which arguments you expect. If the expectations you set aren't met by the time the mock is destroyed, the test fails automatically.

Running tests and measuring coverage

With CTest registration in place, running the whole suite is one command. Internally, GoogleTest's main() simply calls RUN_ALL_TESTS(), which executes every registered test and returns non-zero if any failed:

terminal — run the suite
$ ctest --test-dir build --output-on-failure

   AddTest.HandlesPositiveInput
   StackTest.PopReturnsLastPushed
   IsEvenTest/EvenSamples.ReturnsTrueForEvenNumbers/3
   UserServiceTest.GreetsByName

  100% tests passed, 0 tests failed out of 19

Here's the catch every beginner should internalize early: green tests do not prove your tests are good. A passing suite only tells you the assertions you wrote held. It says nothing about the branches, conditions and decisions your tests never reached. The way to see that gap is to measure code coverage — and not just statement coverage, but branch, condition and, for safety-critical work, MC/DC.

You can measure coverage of a GoogleTest run without changing a single source file. RKTracer auto-detects your compiler and instruments during the build when you prefix your normal build command, then reports against the run produced by CTest:

terminal — coverage of the GTest run
# Prefix your normal build — no source edits, compiler auto-detected
$ rktracer cmake --build build

  compiler: g++ 13.2 (host)
   instrumented calc, calc_tests — source unmodified

$ ctest --test-dir build        # run your GoogleTest suite as usual
$ rkresults --report html

   Statement  100%
   Decision    92%
   Condition   81%  (3 conditions never reached)

That report is the difference between "the tests pass" and "the tests exercise the code." Where RKTracer finds logic your suite never touched, its AI test generation proposes the additional GoogleTest cases — including the boundary inputs — needed to close those gaps, emitting results as rkresults HTML or XML for your CI.

What to remember from this tutorial

  • TEST(Suite, Name) for plain tests, TEST_F for shared fixtures, TEST_P for parameterized runs.
  • EXPECT_* continues on failure; ASSERT_* aborts the test — default to EXPECT.
  • GoogleMock (MOCK_METHOD + EXPECT_CALL) isolates the unit under test from slow or external dependencies.
  • A green suite is necessary but not sufficient — measure coverage to find the logic your tests never reached.

Next steps

You now have the full arc: install with CMake, write a TEST, assert with the right macro, share setup with fixtures, sweep inputs with parameterized tests, isolate dependencies with GoogleMock, and run the lot under CTest. That's enough to write a real, maintainable GoogleTest suite for almost any C++ project.

The natural next move is to close the loop on quality. Wire your suite into CI so it runs on every commit, add coverage measurement so regressions in tested-ness show up as clearly as failing tests, and let coverage point you at the branches still waiting for a test. Green is step one; the gaps are where the bugs hide.

AR
Arjun Rao
Developer Relations, RKValidate

Arjun writes and speaks about C and C++ testing, build systems and coverage tooling, helping teams turn passing test suites into measurable, certifiable quality.

Keep reading
Unit Test Generation

Unit Testing Tools for C, C++, Java and C#

A practical tour of the leading unit test frameworks across four languages — and how to pick one.

Read more
Code Coverage

What is Test-Driven Development (TDD)?

Red, green, refactor — the discipline of writing the test first, and why it shapes better code.

Read more
Code Coverage

Tools for Code Coverage: A Practical Guide

From gcov to qualified analyzers — what each coverage tool measures and where it fits.

Read more

Green tests are step one. Coverage is step two.

See RKTracer measure certifiable coverage of your GoogleTest suite — statement through MC/DC, on host or the real target — and generate tests for the gaps. Book a 30-minute demo or run the free trial today.