Communitygithub.com

C++ 測試

僅在編寫/更新/修復 C++ 測試、配置 GoogleTest/CTest、診斷失敗或不穩定測試,或添加覆蓋率/消毒器時使用。

C++ 測試 是什麼?

此 C++ 測試技能為使用 GoogleTest/GoogleMock 搭配 CMake/CTest 的現代 C++(C++17/20)專案,提供了一個以代理為中心的測試工作流程。它引導開發者遵循 TDD(紅-綠-重構)循環,強調隔離性、依賴注入和 fake 物件設計。該技能涵蓋了編寫基本單元測試、使用測試固件(fixtures)、建立 Google Mock、設定 CMake/CTest 以穩定發現測試、執行測試(包括使用 gtest filter),以及透過重新執行單一測試和啟用消毒器來診斷失敗。它還詳細介紹了使用 GCC+gcov+lcov 和 Clang+llvm-cov 的程式碼覆蓋率設置、AddressSanitizer (ASan)、UndefinedBehaviorSanitizer (UBSan) 和 ThreadSanitizer (TSan) 等消毒器的配置,以及處理不穩定測試的具體防護措施。此外,它還提供了使用 libFuzzer 進行模糊測試和 RapidCheck 進行屬性測試的選用指南,並推薦了 Catch2 和 doctest 作為 GoogleTest 的替代方案。

相容平台~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++ 測試(代理技能)

以代理為中心的測試工作流程,適用於使用 GoogleTest/GoogleMock 搭配 CMake/CTest 的現代 C++(C++17/20)。

使用時機

  • 編寫新的 C++ 測試或修復現有測試
  • 設計 C++ 元件的單元/整合測試覆蓋率
  • 添加測試覆蓋率、CI 閘道或迴歸保護
  • 配置 CMake/CTest 工作流程以確保執行一致性
  • 調查測試失敗或不穩定行為
  • 啟用消毒器以進行記憶體/競爭診斷

何時不應使用

  • 實作不涉及測試變更的新產品功能
  • 與測試覆蓋率或失敗無關的大規模重構
  • 無需測試迴歸驗證的效能調校
  • 非 C++ 專案或非測試任務

核心概念

  • TDD 循環:紅 → 綠 → 重構(先寫測試,最小修正,再清理)。
  • 隔離性:偏好依賴注入和 fake,而非全域狀態。
  • 測試佈局:tests/unit、tests/integration、tests/testdata。
  • 模擬 vs Fake:模擬用於互動,fake 用於狀態行為。
  • 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 &notifier_;
};

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 filter 重新執行單一失敗測試。
  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 進行同步;請使用條件變數或閂鎖(latches)。
  • 為每個測試創建唯一的臨時目錄,並始終清理它們。
  • 在單元測試中避免真實時間、網路或檔案系統依賴。
  • 對隨機化輸入使用確定性種子。

最佳實踐

應做

  • 保持測試確定性和隔離性
  • 優先使用依賴注入而非全域變數
  • 使用 ASSERT_* 作為前置條件,EXPECT_* 進行多重檢查
  • 在 CTest 標籤或目錄中分離單元測試與整合測試
  • 在 CI 中執行消毒器以進行記憶體和競爭檢測

不應做

  • 不要在單元測試中依賴真實時間或網路
  • 不要在使用條件變數時使用 sleep 進行同步
  • 不要過度模擬簡單的值物件
  • 不要對非關鍵日誌使用脆弱的字串匹配

常見陷阱

  • 使用固定的臨時路徑 → 為每個測試生成唯一的臨時目錄並清理它們。
  • 依賴壁鐘時間 → 注入時鐘或使用虛擬時間源。
  • 不穩定的並發測試 → 使用條件變數/閂鎖和有界等待。
  • 隱藏的全域狀態 → 在固件中重置全域狀態或移除全域變數。
  • 過度模擬 → 對狀態行為優先使用 fake,只模擬互動。
  • 缺少消毒器執行 → 在 CI 中添加 ASan/UBSan/TSan 建置。
  • 僅限除錯建置的覆蓋率 → 確保覆蓋率目標使用一致的旗標。

選用附錄:模糊測試 / 屬性測試

僅在專案已支援 LLVM/libFuzzer 或屬性測試庫時使用。

  • libFuzzer:最適合具有最少 I/O 的純函數。
  • RapidCheck:基於屬性的測試,用於驗證不變量。

最小 libFuzzer harness(虛擬程式碼:替換 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.

相關技能