agentleFS
RojiosRojios/everything-claude-code/skills/cpp-testing/SKILL.md

cpp-testing

Skill1 starsChanged 7 months agoMITView on GitHub

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

18 other projects use a version of this, 9 of them edited.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

19 rules. 15 are common, 0 are distinctive, and 4 are very specific to this project.

  • 15

    Common

    Most of the 9 edited versions we read keep these.

  • 0

    Distinctive

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Common:

    Isolation: prefer dependency injection and fakes over global state.

    line 30
  2. Very specific:

    CTest discovery: use gtest_discover_tests() for stable test discovery.

    line 33
  3. Very specific:

    CI signal: run subset first, then full suite with --output-on-failure.

    line 34
  4. Common:

    Follow the RED → GREEN → REFACTOR loop:

    line 38
  5. Common:

    RED: write a failing test that captures the new behavior

    line 40
  6. Common:

    Add scoped logging around the failing assertion.

    line 202
  7. Common:

    Prefer target-level settings instead of global flags.

    line 208
  8. Very specific:

    Never use sleep for synchronization.

    line 268
  9. Common:

    Avoid real time, network, or filesystem dependencies in unit tests.

    line 270
  10. Common:

    Use deterministic seeds for randomized inputs.

    line 271
  11. Common:

    Keep tests deterministic and isolated

    line 277
  12. Common:

    Prefer dependency injection over globals

    line 278
  13. Very specific:

    Use ASSERT_* for preconditions, EXPECT_* for multiple checks

    line 279
  14. Common:

    Run sanitizers in CI for memory and race detection

    line 281
  15. Common:

    Don't depend on real time or network in unit tests

    line 285
  16. Common:

    Don't use sleeps as synchronization when a condition variable can be used

    line 286
  17. Common:

    Don't over-mock simple value objects

    line 287
  18. Common:

    Don't use brittle string matching for non-critical logs

    line 288
  19. Common:

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

    line 302

“Common” is measured: kept by at least 6 in 10 of the 9 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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
// 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.cpp
int Add(int a, int b) { // GREEN
  return a + b;
}

// REFACTOR: simplify/rename once tests pass

Code Examples

Basic Unit Test (gtest)

// 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)

// 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)

// 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

# 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)
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j
ctest --test-dir build --output-on-failure

Running Tests

ctest --test-dir build --output-on-failure
ctest --test-dir build -R ClampTest
ctest --test-dir build -R "UserStoreTest.*" --output-on-failure
./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.

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:

cmake -S . -B build-cov -DENABLE_COVERAGE=ON
cmake --build build-cov -j
ctest --test-dir build-cov
lcov --capture --directory build-cov --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
genhtml coverage.info --output-directory coverage

Clang + llvm-cov:

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

Sanitizers

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):

#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

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.