Skip to main content

cpp-testing

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

Quellinformationen

Repository
loulanyue/awesome-claude-notes
Letzte Quellaktivität
3. Mai 2026 um 21:49
Erkannte Sprache von SKILL.md
Englisch
Sterne
272
Forks
13

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
cpp-testing
description
Use only when writing/updating/fixing C++ tests, configuring GoogleTest/CTest, diagnosing failing or flaky tests, or adding coverage/sanitizers.
source_path
skills/cpp-testing/SKILL.md
origin
ECC
# 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.cpp int 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=Debug cmake --build build -j ctest --test-dir build --output-on-failure ``` ## Running Tests ```bash ctest --test-dir build --output-on-failure ctest --test-dir build -R ClampTest ctest --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=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: ```bash 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 ```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 ## Kaynak belge - [İngilizce kanonik belge](../../../../skills/cpp-testing/SKILL.md) ## Gezinme - [Türkçe dokümantasyon](../../README.md) - [Katkı rehberi](../../../../CONTRIBUTING.md)
Auf GitHub ansehen