ユニットテストの効率を最大化するには、C ++ユニットテストコードをどのように編成する必要がありますか?


47
  • この質問は、単体テストフレームワークに関するものではありません
  • この質問は、単体テストの作成に関するものではありません
  • この質問は約あるところ書かれたUTのコードを配置する方法と、/とき/コンパイルし、それを実行する場所。

レガシーコードを効果的に使用するにあたって、Michael Feathersは次のように主張しています。

優れた単体テスト...高速で実行

そしてそれ

実行に1/10秒かかる単体テストは、低速の単体テストです。

これらの定義は理にかなっていると思います。私はまた、ユニットテストのセットと、より長い時間がかかるコードテストのセットを別々に保持する必要があることを示唆していると思いますが、それが(非常に)高速に実行される場合、ユニットテストと呼ばれるものだけに支払う代償だと思います。

明らかに問題 ++ Cでは、「実行」へのあなたのユニットテスト(複数可)、あなたがする必要があります。

  1. コードを編集します(使用している「サイクル」に応じて、本番またはユニットテスト)
  2. コンパイル
  3. リンク
  4. 単体テスト実行可能ファイルの開始(s

編集(奇妙な近い投票の後):詳細に入る前に、ここでポイントを要約してみます:

(テスト)コードの編集とテストコードの実行の両方が効率的になるように、C ++ユニットテストコードを効果的に編成するにはどうすればよいですか?


最初の問題は、その後、決定することですどこようにユニットテストコードを配置します:

  • 関連する製品コードと組み合わせて編集および表示するのは「自然」です。
  • 現在変更しているユニットのコンパイルサイクルを開始するのは簡単です。

次に、関連する2番目の問題は、フィードバックが瞬時に行われるようにコンパイルするものです。

極端なオプション:

  • 各Unit-Test-Test-Unitは個別のcppファイルに存在し、このcppファイルは(テストするソースコードのユニットファイルと共に)個別にコンパイルおよびリンクされ、単一の実行可能ファイルにリンクされます。
    • (+)これにより、単一のテストユニットの起動(コンパイル+リンク!)時間を最小限に抑えることができます。
    • (+)テストは1つのユニットのみをテストするため、超高速で実行されます。
    • (-)スイート全体を実行するには、膨大な数のプロセスを開始する必要があります。管理することが問題になる場合があります。
    • (-)プロセス開始のオーバーヘッドが見えるようになる
  • 反対側は、テストごとに1つのcppファイルを持っていますが、すべてのテストcppファイルは(テストするコードとともに)1つの実行可能ファイルにリンクされています(モジュールごと/プロジェクトごと/選択を選択してください)。
    • (+)変更されたコードのみがコンパイルされるため、コンパイル時間は問題ありません。
    • (+)実行するexeは1つしかないため、スイート全体の実行は簡単です。
    • (-)任意のオブジェクトを再コンパイルすると再リンクがトリガーされるため、スイートはリンクするのに時間がかかります。
    • (-)(?)スーツの実行に時間がかかります、すべてのユニットテストが高速であれば、時間は問題ないはずです。

それでは、実際のC ++ 単体テストはどのように処理されますか?夜間/毎時だけ実行する場合、2番目の部分は実際には重要ではありませんが、最初の部分、つまりUTコードを本番コードに「結合」する方法です。フォーカスは常に重要だと思います。(そして、開発者がUTコードに焦点を合わせている場合、彼らはそれを実行したいと思うので、パート2に戻ります。)

現実世界の物語と経験に感謝します!

ノート:

  • この質問は、意図しないプラットフォームとメイク/プロジェクトシステムを意図的に残します。
  • 質問タグ付きUT&C ++は開始するのに最適な場所ですが、残念ながらあまりにも多くの質問、特に回答が詳細や特定のフレームワークに集中しすぎています。
  • 少し前に、ブーストユニットテストの構造に関する同様の質問に答えました。この構造は、「実際の」高速な単体テストには欠けていることがわかりました。そして、私は他の質問が狭すぎると思うので、この新しい質問です。

6
コンパイル時にできるだけ多くのエラーを移動するC ++のイディオムによって、さらに複雑な問題が発生します。ユニットテストの優れたスイートでは、特定の使用法がコンパイルに失敗することをテストできる必要があります。

3
@Closers:この質問を終わらせるきっかけとなった議論をお願いします。
リュックトゥレーユ

なぜこれを閉じなければならなかったのか、私にはよくわかりません。:-(このフォーラムにない場合、そのような質問に対する答えをどこで探すと思いますか?

2
@Joe:コンパイラーがとにかくそれを告げるときに何かがコンパイルされるかどうかを見つけるために、なぜユニットテストが必要なのですか?
デビッドソーンリー

2
@David:コンパイルしないことを確認したいからです。簡単な例は、入力を受け入れて出力を生成するパイプラインオブジェクトであり、Pipeline<A,B>.connect(Pipeline<B,C>)コンパイルはすべきですが、コンパイルPipeline<A,B>.connect(Pipeline<C,D>)すべきではありません。最初のステージの出力タイプは、2番目のステージの入力タイプと互換性がありません。
セバスチャンガイガー

回答:


6

1つの実行可能ファイルに(モジュールの)すべての単体テストがあります。テストはグループに分けられます。テストランナーのコマンドラインで(テスト/グループ)名を指定することにより、単一のテスト(またはいくつかのテスト)またはテストのグループを実行できます。ビルドシステムはグループ「ビルド」を実行でき、テスト部門は「すべて」を実行できます。開発者は、「BUG1234」などのグループにいくつかのテストを入れることができます。1234は、作業中のケースの問題追跡番号です。


6

まず、「(1)(本番)コードとユニットテストの編集」に同意しません。一度に1つだけ変更する必要があります。そうしないと、結果が変更された場合、どの原因が原因かわかりません。

メインツリーを隠すディレクトリツリーに単体テストを配置するのが好きです。/sources/componentA/alpha/foo.ccandがある場合/objects/componentA/beta/foo.o/UTest_sources/componentA/alpha/test_foo.ccandのようなものが必要です/UTest_objects/componentA/beta/test_foo.o。スタブ/モックオブジェクトとテストに必要な他のソースには同じシャドウツリーを使用します。いくつかのエッジケースがありますが、このスキームは物事を大幅に簡素化します。優れたエディターマクロを使用すると、テストソースと対象ソースを簡単に取得できます。優れたビルドシステム(例:GNUMake)は両方をコンパイルし、1つのコマンド(例make test_foo)でテストを実行できます。これらのプロセスを開始するオーバーヘッドが問題になることはありませんでした、それはO(N)です。

同じフレームワーク内で、多くのオブジェクトをリンクし、多くのテストを実行する大規模なテスト(ユニットテストではない)を使用できます。トリックは、これらのテストをビルド/実行にかかる時間でソートし、それに応じて毎日のスケジュールに合わせて実行することです。気になるときはいつでも1秒以下のテストを実行します。10秒のテストとストレッチを開始します。5分間のテストと休憩。半時間のテストと昼食に行きます。6時間のテストと家に帰ります。1つの小さなファイルだけを変更した後に巨大なテストを再リンクするなど、多くの時間を無駄にしていることがわかった場合、それは間違っています。リンクが瞬間的であっても、求められなかった。


広告(1)-はい、だらしない言い回し
マーティン・バ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.