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: テストがグリーンのままクリーンアップする
コード例
基本的なユニットテスト(gtest)
フィクスチャ(gtest)
モック(gmock)
CMake/CTest クイックスタート
テストの実行
失敗のデバッグ
- gtest フィルタで単一の失敗したテストを再実行します。
- 失敗したアサーションの周りにスコープ付きログを追加します。
- サニタイザーを有効にして再実行します。
- 根本原因が修正されたら、フルスイートに拡張します。
カバレッジ
グローバルフラグではなく、ターゲットレベルの設定を優先します。
GCC + gcov + lcov:
Clang + llvm-cov:
サニタイザー
フレーキーテストのガードレール
- 同期に
sleepを使用しないでください。条件変数またはラッチを使用してください。 - 一時ディレクトリをテストごとに一意にし、常にクリーンアップしてください。
- ユニットテストで実際の時間、ネットワーク、ファイルシステムの依存関係を避けてください。
- ランダム化された入力には決定論的シードを使用してください。
ベストプラクティス
すべきこと
- テストを決定論的かつ分離されたものに保つ
- グローバル変数よりも依存性注入を優先する
- 前提条件には
ASSERT_*を使用し、複数のチェックにはEXPECT_*を使用する - CTest ラベルまたはディレクトリでユニットテストと統合テストを分離する
- メモリとレース検出のために CI でサニタイザーを実行する
すべきでないこと
- ユニットテストで実際の時間やネットワークに依存しない
- 条件変数を使用できる場合、同期としてスリープを使用しない
- 単純な値オブジェクトをオーバーモックしない
- 重要でないログに脆弱な文字列マッチングを使用しない
よくある落とし穴
- 固定一時パスの使用 → テストごとに一意の一時ディレクトリを生成し、クリーンアップします。
- ウォールクロック時間への依存 → クロックを注入するか、偽の時間ソースを使用します。
- フレーキーな並行性テスト → 条件変数/ラッチと境界付き待機を使用します。
- 隠れたグローバル状態 → フィクスチャでグローバル状態をリセットするか、グローバル変数を削除します。
- オーバーモック → ステートフルな動作にはフェイクを優先し、相互作用のみをモックします。
- サニタイザー実行の欠落 → CI に ASan/UBSan/TSan ビルドを追加します。
- デバッグのみのビルドでのカバレッジ → カバレッジターゲットが一貫したフラグを使用することを確認します。
オプションの付録: ファジングとプロパティテスト
プロジェクトがすでに LLVM/libFuzzer またはプロパティテストライブラリをサポートしている場合にのみ使用してください。
- libFuzzer: 最小限の I/O で純粋関数に最適です。
- RapidCheck: 不変条件を検証するプロパティベースのテストです。
最小限の libFuzzer ハーネス(擬似コード: ParseConfig を置き換えてください):
GoogleTest の代替
- Catch2: ヘッダーオンリー、表現力豊かなマッチャー
- doctest: 軽量、最小限のコンパイルオーバーヘッド


