Communitygithub.com

C++ 测试

仅用于编写/更新/修复 C++ 测试、配置 GoogleTest/CTest、诊断失败或易变测试、以及添加覆盖率或消毒器。

C++ 测试 是什么?

此技能专为 AI 代理(如 Claude Code、Cursor 或 Codex)设计,用于在 C++ 项目中使用 GoogleTest/GoogleMock 和 CMake/CTest 高效地处理测试任务。它覆盖了完整的测试工作流,包括编写新测试、修复现有测试、配置构建、诊断失败或易变测试,以及启用代码覆盖率和消毒器。技能遵循经典的 TDD 循环(红/绿/重构),强调隔离和依赖注入,并提供了从基本单元测试、夹具、模拟对象到 CMake 集成、覆盖率收集(GCC/lcov、Clang/llvm-cov)和消毒器(AddressSanitizer、UndefinedBehaviorSanitizer、ThreadSanitizer)的详细示例。它还包含了处理易变测试的防护措施和常见陷阱的指导,使其成为希望将 C++ 测试工作流自动化和标准化的开发者的全面指南。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/cpp-testing

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

C++ 测试(代理技能)

针对现代 C++(C++17/20)使用 GoogleTest/GoogleMock 和 CMake/CTest 的代理级测试工作流。

何时使用

  • 编写新的 C++ 测试或修复现有测试
  • 为 C++ 组件设计单元/集成测试覆盖
  • 添加测试覆盖、CI 门控或回归保护
  • 配置 CMake/CTest 工作流以实现一致执行
  • 调查测试失败或易变行为
  • 启用消毒器进行内存/竞争诊断

何时不使用

  • 实现没有测试变更的新产品功能
  • 与测试覆盖或失败无关的大规模重构
  • 没有测试回归验证的性能调优
  • 非 C++ 项目或非测试任务

核心概念

  • TDD 循环:红 → 绿 → 重构(先写测试,最小化修复,然后清理)
  • 隔离:优先使用依赖注入和假对象,避免全局状态
  • 测试布局:tests/unit,tests/integration,tests/testdata
  • 模拟 vs 假对象:模拟用于交互,假对象用于有状态行为
  • CTest 发现:使用 gtest_discover_tests() 进行稳定的测试发现
  • CI 信号:先运行子集,再用 --output-on-failure 运行全套

TDD 工作流

遵循 红 → 绿 → 重构 循环:

  1. 红:编写一个失败的测试,捕获新行为
  2. 绿:实现最小的更改以通过测试
  3. 重构:在测试保持绿色的同时清理代码
// tests/add_test.cpp
#include <gtest/gtest.h>

int Add(int a, int b); // 由生产代码提供。

TEST(AddTest, AddsTwoNumbers) { // 红
  EXPECT_EQ(Add(2, 3), 5);
}

// src/add.cpp
int Add(int a, int b) { // 绿
  return a + b;
}

// 重构:测试通过后简化/重命名

代码示例

基本单元测试 (gtest)

// tests/calculator_test.cpp
#include <gtest/gtest.h>

int Add(int a, int b); // 由生产代码提供。

TEST(CalculatorTest, AddsTwoNumbers) {
    EXPECT_EQ(Add(2, 3), 5);
}

夹具 (gtest)

// tests/user_store_test.cpp
// 伪代码桩:将 UserStore/User 替换为项目类型。
#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");
}

模拟 (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 ¬ifier_;
};

TEST(ServiceTest, SendsNotifications) {
    MockNotifier notifier;
    Service service(notifier);

    EXPECT_CALL(notifier, Send("hello")).Times(1);
    service.Publish("hello");
}

CMake/CTest 快速入门

# CMakeLists.txt (摘录)
cmake_minimum_required(VERSION 3.20)
project(example LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
# 优先使用项目锁定的版本。如果使用标签,请根据项目策略使用固定版本。
set(GTEST_VERSION v1.17.0) # 根据项目策略调整。
FetchContent_Declare(
  googletest
  # Google Test 框架(官方仓库)
  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

运行测试

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

调试失败

  1. 使用 gtest 过滤器重新运行单个失败的测试。
  2. 在失败的断言周围添加作用域日志。
  3. 在启用消毒器的情况下重新运行。
  4. 找到根本原因后扩展到完整套件。

覆盖率

优先使用目标级设置而不是全局标志。

option(ENABLE_COVERAGE "启用覆盖率标志" 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

消毒器

option(ENABLE_ASAN "启用 AddressSanitizer" OFF)
option(ENABLE_UBSAN "启用 UndefinedBehaviorSanitizer" OFF)
option(ENABLE_TSAN "启用 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()

易变测试防护措施

  • 永远不要使用 sleep 进行同步;改用条件变量或闩锁。
  • 为每个测试创建唯一的临时目录,并始终清理它们。
  • 在单元测试中避免真实时间、网络或文件系统依赖。
  • 对随机输入使用确定性种子。

最佳实践

应该做

  • 保持测试确定性和隔离性
  • 优先使用依赖注入而不是全局变量
  • 使用 ASSERT_* 进行前置条件检查,EXPECT_* 进行多项检查
  • 在 CTest 标签或目录中分离单元测试和集成测试
  • 在 CI 中运行消毒器以进行内存和竞争检测

不应该做

  • 不要在单元测试中依赖真实时间或网络
  • 不要使用 sleep 作为同步手段,而应使用条件变量
  • 不要过度模拟简单的值对象
  • 不要对非关键日志使用脆弱的字符串匹配

常见陷阱

  • 使用固定临时路径 → 为每个测试生成唯一的临时目录并清理。
  • 依赖挂钟时间 → 注入时钟或使用虚假时间源。
  • 易变的并发测试 → 使用条件变量/闩锁和有界等待。
  • 隐藏的全局状态 → 在夹具中重置全局状态或移除全局变量。
  • 过度模拟 → 对有状态行为优先使用假对象,仅模拟交互。
  • 缺少消毒器运行 → 在 CI 中添加 ASan/UBSan/TSan 构建。
  • 仅调试构建的覆盖率 → 确保覆盖率目标使用一致的标志。

可选附录:模糊测试 / 属性测试

仅当项目已支持 LLVM/libFuzzer 或属性测试库时使用。

  • libFuzzer:最适合纯函数且 I/O 最低的场景。
  • RapidCheck:基于属性的测试,用于验证不变量。

最小的 libFuzzer 测试桩(伪代码:替换 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); // 项目函数
    return 0;
}

GoogleTest 的替代方案

  • Catch2:仅头文件,表达性匹配器
  • doctest:轻量级,最小编译开销

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能