C と C++ のテストは実行可能プログラムであり、そのステータスがビルドと CI の結果に影響しなければなりません。フレームワークはアサーションとレポートを改善できますが、専用の main と決定論的な依存関係を持つテスト実行ファイルをプロジェクトのテストランナーに統合すれば十分です。
テストを第一級ターゲットとしてビルドする #
テストターゲットは本番バイナリとは別にコンパイルし、本番ライブラリにリンクします。CTest、Meson、Automake の TESTS など、既存のテストランナーにすべての実行ファイルを登録し、ゼロ以外の終了ステータスで CI が失敗するようにします。
add_executable(parser_test parser_test.cc)
target_link_libraries(parser_test PRIVATE parser)
add_test(NAME parser COMMAND parser_test)可能な場合は、テスト専用のフィクスチャーとフェイクを本番インターフェイスの外に置きます。テストランナーは失敗したケースを報告し、診断情報を保持し、必要なアサーションまたはプロセスが失敗した場合はゼロ以外を返すべきです。
観測可能な動作をテストする #
非公開の表現ではなく、公開された動作、エラー報告、クリーンアップ、境界条件をテストします。カバレッジを次の範囲で整理します。
- 単体テストは、制御された協調コンポーネントを使って小さなコンポーネントを検証する。
- 統合テストは、シリアライゼーション、データベースアダプター、パーサーとスキャナーなど、実際のコンポーネント間の境界を検証する。
- エンドツーエンドテストは、組み立てられた実行ファイルまたはサービス契約を検証する。
空の入力、上限、無効なエンコーディング、注入可能な割り当てまたは I/O の失敗、所有権の遷移、以前に修正した不具合の回帰を含めます。テストは偶然の実装詳細ではなく、保証された結果をアサートすべきです。
依存関係を決定論的にする #
クロック、乱数、ファイルシステム、子プロセス、ネットワーククライアントを注入またはラップします。失敗した乱数シードを記録し、再現可能にします。テストハーネスが管理する一時テストワークスペース、一意のポートまたはメモリ内トランスポート、上限付きタイムアウトを使用します。共有環境状態とテスト順序への依存を避けます。
破壊的なテストやクラッシュする可能性のあるテストはサブプロセスで実行します。プロセス分離により、終了ステータス、シグナル、標準出力、標準エラー、コマンドライン動作も直接アサートできます。
テストと併せて実行時診断を使用する #
サニタイザーは実行されたパス上の欠陥の種類を検出するため、機能テストを置き換えるのではなく補完します。
c++ -fsanitize=address,undefined -fno-omit-frame-pointer -g \
parser_test.cc -o parser_test
./parser_testAddressSanitizer は多くの無効なメモリアクセスを検出し、UndefinedBehaviorSanitizer は選択された未定義の操作を診断します。CI でプロジェクトがサポートするサニタイザー設定を有効にし、シンボル化されたスタックとログを保持し、サニタイザーの報告をテスト失敗として扱います。
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)