cpp-testing

安装量: 865
排名: #1494

安装

npx skills add https://github.com/affaan-m/everything-claude-code --skill cpp-testing
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:
RED
write a failing test that captures the new behavior
GREEN
implement the smallest change to pass
REFACTOR
clean up while tests stay green // tests/add_test.cpp

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

include

include

include 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

include

include 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
Re-run the single failing test with gtest filter.
Add scoped logging around the failing assertion.
Re-run with sanitizers enabled.
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

include

include
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
返回排行榜