Communitygithub.com

C++ 테스팅

GoogleTest/CTest를 사용한 현대 C++ (C++17/20) 테스트 작성, 수정, 진단, 커버리지 및 샌타타이저 설정을 위한 에이전트 스킬입니다.

C++ 테스팅란 무엇인가요?

이 스킬은 AI 에이전트(Claude Code, Cursor, Codex 등)가 C++ 프로젝트에서 테스트 관련 작업을 수행할 때 따라야 할 체계적인 워크플로우와 모범 사례를 제공합니다. 특히 GoogleTest/GoogleMock과 CMake/CTest를 기반으로 한 TDD(레드-그린-리팩터) 사이클, 단위/통합 테스트 격리, 목(Mock)과 페이크(Fake) 사용법, 테스트 레이아웃 규칙 등을 상세히 설명합니다. 또한 CTest를 통한 테스트 발견 및 실행, 실패 진단 방법, 코드 커버리지(GCC/lcov, Clang/llvm-cov) 및 샌타타이저(ASan, UBSan, TSan) 활성화 방법도 포함합니다. 플래키(Flaky) 테스트 방지 가이드라인과 Catch2, doctest 같은 대안 프레임워크도 언급되어 있어, 에이전트가 C++ 테스팅 전반을 일관되고 생산적으로 수행할 수 있도록 돕습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/cpp-testing

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

C++ 테스팅 (에이전트 스킬)

GoogleTest/GoogleMock과 CMake/CTest를 사용한 현대 C++(C++17/20)을 위한 에이전트 중심 테스팅 워크플로우입니다.

사용 시기

  • 새 C++ 테스트 작성 또는 기존 테스트 수정
  • C++ 컴포넌트에 대한 단위/통합 테스트 커버리지 설계
  • 테스트 커버리지, CI 게이팅 또는 회귀 방지 추가
  • 일관된 실행을 위한 CMake/CTest 워크플로우 구성
  • 테스트 실패 또는 플래키(flaky) 동작 조사
  • 메모리/경쟁 상태 진단을 위한 샌타타이저 활성화

사용하지 말아야 할 경우

  • 테스트 변경 없이 새 제품 기능 구현
  • 테스트 커버리지나 실패와 관련 없는 대규모 리팩터링
  • 테스트 회귀 검증 없이 성능 튜닝
  • C++이 아닌 프로젝트 또는 테스트가 아닌 작업

핵심 개념

  • TDD 루프: 레드 → 그린 → 리팩터 (테스트 우선, 최소 수정, 그 후 정리)
  • 격리: 전역 상태보다 의존성 주입과 페이크 선호
  • 테스트 레이아웃: tests/unit, tests/integration, tests/testdata
  • 목 vs 페이크: 상호작용 확인에는 목, 상태 유지 동작에는 페이크 사용
  • CTest 발견: gtest_discover_tests()로 안정적인 테스트 발견
  • CI 신호: 먼저 부분 집합을 실행한 후 --output-on-failure로 전체 스위트 실행

TDD 워크플로우

RED → GREEN → REFACTOR 루프를 따릅니다:

  1. RED: 새 동작을 포착하는 실패하는 테스트 작성
  2. GREEN: 통과시키기 위한 최소한의 변경 구현
  3. REFACTOR: 테스트가 통과하는 상태에서 코드 정리
// tests/add_test.cpp
#include <gtest/gtest.h>

int Add(int a, int b); // 프로덕션 코드에서 제공.

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

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

// REFACTOR: 테스트 통과 후 단순화/이름 변경

코드 예제

기본 단위 테스트 (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 필터로 실패한 단일 테스트를 다시 실행합니다.
  2. 실패한 어설션 주변에 범위가 지정된 로깅을 추가합니다.
  3. 샌타타이저를 활성화하여 다시 실행합니다.
  4. 근본 원인이 해결되면 전체 스위트로 확장합니다.

커버리지

전역 플래그보다는 타겟 수준 설정을 선호합니다.

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

샌타타이저

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) 테스트 방지 가이드라인

  • 동기화를 위해 sleep을 절대 사용하지 말고, 조건 변수 또는 래치를 사용하세요.
  • 임시 디렉토리는 테스트마다 고유하게 생성하고 항상 정리하세요.
  • 단위 테스트에서 실제 시간, 네트워크 또는 파일 시스템 의존성을 피하세요.
  • 무작위 입력에는 결정론적 시드를 사용하세요.

모범 사례

따라야 할 것 (DO)

  • 테스트를 결정론적이고 격리되게 유지하세요.
  • 전역 변수보다 의존성 주입을 선호하세요.
  • 전제 조건에는 ASSERT_*, 여러 검사에는 EXPECT_*를 사용하세요.
  • CTest 레이블이나 디렉토리로 단위 테스트와 통합 테스트를 분리하세요.
  • CI에서 메모리 및 경쟁 상태 탐지를 위해 샌타타이저를 실행하세요.

하지 말아야 할 것 (DON'T)

  • 단위 테스트에서 실제 시간이나 네트워크에 의존하지 마세요.
  • 조건 변수를 사용할 수 있는데 동기화에 sleep을 사용하지 마세요.
  • 단순 값 객체를 과도하게 목(mock)하지 마세요.
  • 중요하지 않은 로그에 대해 취약한 문자열 매칭을 사용하지 마세요.

일반적인 함정

  • 고정된 임시 경로 사용 → 테스트마다 고유한 임시 디렉토리를 생성하고 정리하세요.
  • 실제 시간에 의존 → 클록을 주입하거나 가짜 시간 소스를 사용하세요.
  • 플래키 동시성 테스트 → 조건 변수/래치와 제한된 대기를 사용하세요.
  • 숨겨진 전역 상태 → 픽스처에서 전역 상태를 재설정하거나 제거하세요.
  • 과도한 목(mock) 사용 → 상태 유지 동작에는 페이크를 선호하고 상호작용만 목(mock)하세요.
  • 샌타타이저 실행 누락 → 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.

관련 스킬