🛠️ Cppテスト
C++言語で開発されたプログラムが正し??
📺 まず動画で見る(YouTube)
▶ 【衝撃】最強のAIエージェント「Claude Code」の最新機能・使い方・プログラミングをAIで効率化する超実践術を解説! ↗
※ jpskill.com 編集部が参考用に選んだ動画です。動画の内容と Skill の挙動は厳密には一致しないことがあります。
📜 元の英語説明(参考)
C++ テストの作成/更新/修正、GoogleTest/CTest の設定、失敗またはフレーキーなテストの診断、カバレッジ/サニタイザーの追加時にのみ使用します。
🇯🇵 日本人クリエイター向け解説
C++言語で開発されたプログラムが正し??
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-17
- 取得日時
- 2026-05-17
- 同梱ファイル
- 1
💬 こう話しかけるだけ — サンプルプロンプト
- › Cpp Testing を使って、最小構成のサンプルコードを示して
- › Cpp Testing の主な使い方と注意点を教えて
- › Cpp Testing を既存プロジェクトに組み込む方法を教えて
これをClaude Code に貼るだけで、このSkillが自動発動します。
📖 Claude が読む原文 SKILL.md(中身を展開)
この本文は AI(Claude)が読むための原文(英語または中国語)です。日本語訳は順次追加中。
C++ Testing(エージェントスキル)
CMake/CTest を使用した GoogleTest/GoogleMock による最新の C++(C++17/20)向けのエージェント重視のテストワークフローです。
使用タイミング
- 新しい C++ テストの作成または既存のテストの修正
- C++ コンポーネントのユニット/統合テストカバレッジの設計
- テストカバレッジ、CI ゲーティング、リグレッション保護の追加
- 一貫した実行のための CMake/CTest ワークフローの設定
- テスト失敗またはフレーキーな動作の調査
- メモリ/レース診断のためのサニタイザーの有効化
使用すべきでない場合
- テスト変更を伴わない新しい製品機能の実装
- テストカバレッジや失敗に関連しない大規模なリファクタリング
- 検証するテストリグレッションのないパフォーマンスチューニング
- C++ 以外のプロジェクトまたはテスト以外のタスク
コア概念
- TDD ループ: red → green → refactor(テスト優先、最小限の修正、その後クリーンアップ)
- 分離: グローバル状態よりも依存性注入とフェイクを優先
- テストレイアウト:
tests/unit、tests/integration、tests/testdata - モック vs フェイク: 相互作用にはモック、ステートフルな動作にはフェイク
- CTest ディスカバリー: 安定したテストディスカバリーのために
gtest_discover_tests()を使用 - CI シグナル: 最初にサブセットを実行し、次に
--output-on-failureでフルスイートを実行
TDD ワークフロー
RED → GREEN → REFACTOR ループに従います:
- RED: 新しい動作をキャプチャする失敗するテストを書く
- GREEN: 合格する最小限の変更を実装する
- 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 ¬ifier) : 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
URL Google Test framework (official repository) 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
失敗のデバッグ
- gtest フィルタで単一の失敗したテストを再実行します。
- 失敗したアサーションの周りにスコープ付きログを追加します。
- サニタイザーを有効にして再実行します。
- 根本原因が修正されたら、フルスイートに拡張します。
カバレッジ
グローバルフラグではなく、ターゲットレベルの設定を優先します。
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()
フレーキーテストのガードレール
- 同期に
sleepを使用しないでください。条件変数またはラッチを使用してください。 - 一時ディレクトリをテストごとに一意にし、常にクリーンアップしてください。
- ユニットテストで実際の時間、ネットワーク、ファイルシステムの依存関係を避けてください。
- ランダム化された入力には決定論的シードを使用してください。
ベストプラクティス
すべきこと
- テストを決定論的かつ分離されたものに保つ
- グローバル変数よりも依存性注入を優先する
- 前提条件には
ASSERT_*を使用し、複数のチェックにはEXPECT_*を使用する - CTest ラベルまたはディレクトリでユニットテストと統合テストを分離する
- メモリとレース検出のために CI でサニタイザーを実行する
すべきでないこと
- ユニットテストで実際の時間やネットワークに依存しない
- 条件変数を使用できる場合、同期としてスリープを使用しない
- 単純な値オブジェクトをオーバーモックしない
- 重要でないログに脆弱な文字列マッチングを使用しない
よくある落とし穴
- 固定一時パスの使用 → テストごとに一意の一時ディレクトリを生成し、クリーンアップします。
- ウォールクロック時間への依存 → クロックを注入するか、偽の時間ソースを使用します。
- フレーキーな並行性テスト → 条件変数/ラッチと境界付き待機を使用します。
- 隠れたグローバル状態 → フィクスチャでグローバル状態をリセットするか、グローバル変数を削除します。
- オーバーモック → ステートフルな動作にはフェイクを優先し、相互作用のみをモックします。
- サニタイザー実行の欠落 → 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: 軽量、最小限のコンパイルオーバーヘッド