Cpp Testing

by affaan-mef648e01899bNo license275K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 3 days ago

Use only when writing/updating/fixing C++ tests, configuring GoogleTest/CTest, diagnosing failing or flaky tests, or adding coverage/sanitizers.

Instructions onlySoftware Development
AI-generated overview

Guides writing, fixing and debugging modern C++ tests with GoogleTest, CMake/CTest, coverage and sanitizers.

What it does
This skill provides an agent workflow for C++17/20 testing with GoogleTest/GoogleMock and CMake/CTest. It covers a red-green-refactor TDD loop, test layout, fixtures, mocks versus fakes, CTest discovery, running and filtering tests, debugging failures, coverage with gcov/lcov or llvm-cov, and sanitizer builds. It also lists flaky-test guardrails, best practices, common pitfalls, and optional fuzzing or property-testing notes.
When to use it
Use it when writing, updating or fixing C++ tests, designing unit or integration coverage, configuring CMake/CTest, or investigating failing or flaky tests. It also fits adding coverage, CI gating, regression protection, or sanitizer diagnostics for memory and race issues.
Requirements
Instructions only; no scripts are shipped. It assumes a C++17/20 toolchain, CMake 3.20 or later, and GoogleTest/GoogleMock (fetched via FetchContent in the examples). Coverage and sanitizer sections need GCC or Clang with gcov/lcov or llvm-cov, and network access may be needed to fetch dependencies.

C++ Testing (Agent Skill)

Agent-focused testing workflow for modern C++ (C++17/20) using GoogleTest/GoogleMock with CMake/CTest.

When to Use

  • Writing new C++ tests or fixing existing tests
  • Designing unit/integration test coverage for C++ components
  • Adding test coverage, CI gating, or regression protection
  • Configuring CMake/CTest workflows for consistent execution
  • Investigating test failures or flaky behavior
  • Enabling sanitizers for memory/race diagnostics

When NOT to Use

  • Implementing new product features without test changes
  • Large-scale refactors unrelated to test coverage or failures
  • Performance tuning without test regressions to validate
  • Non-C++ projects or non-test tasks

Core Concepts

  • TDD loop: red → green → refactor (tests first, minimal fix, then cleanups).
  • Isolation: prefer dependency injection and fakes over global state.
  • Test layout: tests/unit, tests/integration, tests/testdata.
  • Mocks vs fakes: mock for interactions, fake for stateful behavior.
  • CTest discovery: use gtest_discover_tests() for stable test discovery.
  • CI signal: run subset first, then full suite with --output-on-failure.

TDD Workflow

Follow the RED → GREEN → REFACTOR loop:

  1. RED: write a failing test that captures the new behavior
  2. GREEN: implement the smallest change to pass
  3. REFACTOR: clean up while tests stay green
cpp
// tests/add_test.cpp#include <gtest/gtest.h>
int Add(int a, int b); // Provided by production code.
TEST(AddTest, AddsTwoNumbers) { // RED  EXPECT_EQ(Add(2, 3), 5);}
// src/add.cppint Add(int a, int b) { // GREEN  return a + b;}
// REFACTOR: simplify/rename once tests pass

Code Examples

Basic Unit Test (gtest)

cpp
// tests/calculator_test.cpp#include <gtest/gtest.h>
int Add(int a, int b); // Provided by production code.
TEST(CalculatorTest, AddsTwoNumbers) {    EXPECT_EQ(Add(2, 3), 5);}

Fixture (gtest)

cpp
// tests/user_store_test.cpp// Pseudocode stub: replace UserStore/User with project types.#include <gtest/gtest.h>#include <memory>#include <optional>#include <string>
struct User { std::string name; };class UserStore {public:    explicit UserStore(std::string /*path*/) {}    void Seed(std::initializer_list<User> /*users*/) {}    std::optional<User> Find(const std::string &/*name*/) { return User{"alice"}; }};
class UserStoreTest : public ::testing::Test {protected:    void SetUp() override {        store = std::make_unique<UserStore>(":memory:");        store->Seed({{"alice"}, {"bob"}});    }
    std::unique_ptr<UserStore> store;};
TEST_F(UserStoreTest, FindsExistingUser) {    auto user = store->Find("alice");    ASSERT_TRUE(user.has_value());    EXPECT_EQ(user->name, "alice");}

Mock (gmock)

cpp
// tests/notifier_test.cpp#include <gmock/gmock.h>#include <gtest/gtest.h>#include <string>
class Notifier {public:    virtual ~Notifier() = default;    virtual void Send(const std::string &message) = 0;};
class MockNotifier : public Notifier {public:    MOCK_METHOD(void, Send, (const std::string &message), (override));};
class Service {public:    explicit Service(Notifier &notifier) : notifier_(notifier) {}    void Publish(const std::string &message) { notifier_.Send(message); }
private:    Notifier &notifier_;};
TEST(ServiceTest, SendsNotifications) {    MockNotifier notifier;    Service service(notifier);
    EXPECT_CALL(notifier, Send("hello")).Times(1);    service.Publish("hello");}

CMake/CTest Quickstart

cmake
# CMakeLists.txt (excerpt)cmake_minimum_required(VERSION 3.20)project(example LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)# Prefer project-locked versions. If using a tag, use a pinned version per project policy.set(GTEST_VERSION v1.17.0) # Adjust to project policy.FetchContent_Declare(  googletest  # Google Test framework (official repository)  URL https://github.com/google/googletest/archive/refs/tags/${GTEST_VERSION}.zip)FetchContent_MakeAvailable(googletest)
add_executable(example_tests  tests/calculator_test.cpp  src/calculator.cpp)target_link_libraries(example_tests GTest::gtest GTest::gmock GTest::gtest_main)
enable_testing()include(GoogleTest)gtest_discover_tests(example_tests)
bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debugcmake --build build -jctest --test-dir build --output-on-failure

Running Tests

bash
ctest --test-dir build --output-on-failurectest --test-dir build -R ClampTestctest --test-dir build -R "UserStoreTest.*" --output-on-failure
bash
./build/example_tests --gtest_filter=ClampTest.*./build/example_tests --gtest_filter=UserStoreTest.FindsExistingUser

Debugging Failures

  1. Re-run the single failing test with gtest filter.
  2. Add scoped logging around the failing assertion.
  3. Re-run with sanitizers enabled.
  4. Expand to full suite once the root cause is fixed.

Coverage

Prefer target-level settings instead of global flags.

cmake
option(ENABLE_COVERAGE "Enable coverage flags" OFF)
if(ENABLE_COVERAGE)  if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")    target_compile_options(example_tests PRIVATE --coverage)    target_link_options(example_tests PRIVATE --coverage)  elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")    target_compile_options(example_tests PRIVATE -fprofile-instr-generate -fcoverage-mapping)    target_link_options(example_tests PRIVATE -fprofile-instr-generate)  endif()endif()

GCC + gcov + lcov:

bash
cmake -S . -B build-cov -DENABLE_COVERAGE=ONcmake --build build-cov -jctest --test-dir build-covlcov --capture --directory build-cov --output-file coverage.infolcov --remove coverage.info '/usr/*' --output-file coverage.infogenhtml coverage.info --output-directory coverage

Clang + llvm-cov:

bash
cmake -S . -B build-llvm -DENABLE_COVERAGE=ON -DCMAKE_CXX_COMPILER=clang++cmake --build build-llvm -jLLVM_PROFILE_FILE="build-llvm/default.profraw" ctest --test-dir build-llvmllvm-profdata merge -sparse build-llvm/default.profraw -o build-llvm/default.profdatallvm-cov report build-llvm/example_tests -instr-profile=build-llvm/default.profdata

Sanitizers

cmake
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)option(ENABLE_TSAN "Enable ThreadSanitizer" OFF)
if(ENABLE_ASAN)  add_compile_options(-fsanitize=address -fno-omit-frame-pointer)  add_link_options(-fsanitize=address)endif()if(ENABLE_UBSAN)  add_compile_options(-fsanitize=undefined -fno-omit-frame-pointer)  add_link_options(-fsanitize=undefined)endif()if(ENABLE_TSAN)  add_compile_options(-fsanitize=thread)  add_link_options(-fsanitize=thread)endif()

Flaky Tests Guardrails

  • Never use sleep for synchronization; use condition variables or latches.
  • Make temp directories unique per test and always clean them.
  • Avoid real-time, network, or filesystem dependencies in unit tests.
  • Use deterministic seeds for randomized inputs.

Best Practices

DO

  • Keep tests deterministic and isolated
  • Prefer dependency injection over globals
  • Use ASSERT_* for preconditions, EXPECT_* for multiple checks
  • Separate unit vs integration tests in CTest labels or directories
  • Run sanitizers in CI for memory and race detection

DON'T

  • Don't depend on real time or network in unit tests
  • Don't use sleeps as synchronization when a condition variable can be used
  • Don't over-mock simple value objects
  • Don't use brittle string matching for non-critical logs

Common Pitfalls

  • Using fixed temp paths → Generate unique temp directories per test and clean them.
  • Relying on wall clock time → Inject a clock or use fake time sources.
  • Flaky concurrency tests → Use condition variables/latches and bounded waits.
  • Hidden global state → Reset global state in fixtures or remove globals.
  • Over-mocking → Prefer fakes for stateful behavior and only mock interactions.
  • Missing sanitizer runs → Add ASan/UBSan/TSan builds in CI.
  • Coverage on debug-only builds → Ensure coverage targets use consistent flags.

Optional Appendix: Fuzzing / Property Testing

Only use if the project already supports LLVM/libFuzzer or a property-testing library.

  • libFuzzer: best for pure functions with minimal I/O.
  • RapidCheck: property-based tests to validate invariants.

Minimal libFuzzer harness (pseudocode: replace ParseConfig):

cpp
#include <cstddef>#include <cstdint>#include <string>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {    std::string input(reinterpret_cast<const char *>(data), size);    // ParseConfig(input); // project function    return 0;}

Alternatives to GoogleTest

  • Catch2: header-only, expressive matchers
  • doctest: lightweight, minimal compile overhead

Source and attribution

Source:affaan-m/eccin.kiro/skills/cpp-testingat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal