web-dev-qa-db-ja.com

アサートまたは単体テストの方が重要ですか?

アサートと単体テストはどちらもコードベースのドキュメントとして機能し、バグを発見する手段としても機能します。主な違いは、アサートが正常性チェックとして機能し、実際の入力を確認するのに対し、単体テストは特定のシミュレートされた入力で実行され、明確に定義された単一の「正解」に対するテストです。正当性を検証する主な手段として、アサートとユニットテストを使用することの相対的なメリットは何ですか?あなたはどちらをもっと強調すべきだと思いますか?

52
dsimcha

Assertsは、-プログラムの内部状態について通知するのに役立ちます。たとえば、データ構造に有効な状態があること、たとえば、Timeデータ構造が_25:61:61_の値を保持しないことなどです。アサートによってチェックされる条件は次のとおりです。

  • 発信者が契約を維持することを保証する前提条件、

  • 呼び出し先が契約を維持することを保証する事後条件、および

  • 不変条件。これにより、関数が戻った後もデータ構造が常に何らかのプロパティを保持することが保証されます。不変条件は、前提条件と事後条件である条件です。

ユニットテストは、モジュールの外部動作について説明するのに役立ちます。 Stackは、Push()メソッドが呼び出された後に一貫した状態になる可能性がありますが、スタックのサイズが3回呼び出された後に3ずつ増加しない場合、それはエラーです。 (たとえば、誤ったPush()実装がアサートをチェックして終了するという些細なケース)

厳密に言えば、アサートとユニットテストの主な違いは、アサートでは行われないのに対し、ユニットテストにはテストデータがある(プログラムを実行するための値)です。つまり、ユニットテストを自動的に実行できますが、アサーションについては同じことは言えません。この説明のために、私はあなたが高次関数テスト(プログラム全体を実行し、単体テストのようなモジュールを駆動しない)のコンテキストでプログラムを実行することについて話していると仮定しました。 「実際の入力を確認する」手段として自動化された機能テストについて話していない場合、その価値は明らかに自動化にあるため、単体テストが有利です。 (自動化された)関数テストのコンテキストでこれについて話している場合は、以下を参照してください。

テスト対象が重複している可能性があります。たとえば、Stackの事後条件は、スタックサイズが1増えると実際にアサートする場合があります。しかし、そのアサートで実行できることには制限があります。最上位の要素が追加されたばかりであることも確認する必要がありますか?

どちらの場合も、品質を高めることが目標です。単体テストの目的は、バグを見つけることです。アサーションの場合、目標は、プログラムの無効な状態が発生するとすぐに監視することで、デバッグを容易にすることです。

どちらでもない手法で正確性を検証することに注意してください。実際、プログラムが正しいことを確認することを目的として単体テストを実施する場合、おそらく、機能することがわかっている面白くないテストを思い付くでしょう。それは心理的な効果です。あなたは自分の目標を達成するために何でもします。あなたの目標がバグを見つけることである場合、あなたの活動はそれを反映します。

どちらも重要であり、独自の目的があります。

[アサーションに関する最後のメモとして:最大の価値を得るには、いくつかの主要な関数ではなく、プログラムのすべての重要なポイントでそれらを使用する必要があります。そうしないと、問題の元の原因がマスクされ、何時間ものデバッグを行わないと検出が困難になる可能性があります。]

43
Macneil

アサーションについて話すときは、スイッチを押すだけでオフにできることに注意してください。

非常に悪いアサーションの例:

_char *c = malloc(1024);
assert(c != NULL);
_

なぜこれが悪いのですか? NDEBUGなどが定義されているためにそのアサーションがスキップされた場合、エラーチェックは行われないためです。

単体テストでは、上記のコードで(おそらく)segfaultが発生します。確かに、何かがうまくいかなかったと言って仕事をしましたか?テストでmalloc()が失敗する可能性はどのくらいありますか?

アサーションは、「通常の」イベントによってアサーションが起動されないことをプログラマが示す必要がある場合のデバッグ用です。 malloc() failingは、実際には通常のイベントであるため、アサートしてはなりません。

間違っている可能性のあるものを適切に処理する代わりにアサーションが使用される他の多くのケースがあります。これがアサーションの評判が悪く、Goなどの言語に含まれていない理由です。

ユニットテストは、変更した何かが他の何かを壊したときに通知するように設計されています。 (ほとんどの場合)ビルドを行うたびにプログラムのすべての機能(ただし、テスターareリリースにとって重要)を実行する必要がないように設計されています。

両者が何かがうまくいかなかったことを告げることを除いて、2つの間には実際に明確な相関関係はありません。デバッガーを使用せずに、アサートを作業中の何かのブレークポイントと考えてください。単体テストは、あなたが何かを壊した場合にそれを知らせるものと考えてくださいではない作業中。

7
Tim Post

どちらも、構築しているシステムの全体的な品質を向上させるのに役立つツールです。多くは、使用している言語、構築しているアプリケーションのタイプ、および時間を最もよく費やす場所に依存します。言うまでもなく、あなたはそれについていくつかの考えを持っています。

まず、assertキーワードのない言語を使用している場合、アサートを使用することはできません(少なくとも、ここで言う方法では)。長い間、Javaにはassertキーワードがありませんでしたが、多くの言語にはまだありません。その場合、ユニットテストはかなり重要になります。一部の言語ではアサーションは、フラグが設定されている場合にのみ実行されます(ここでもJavaがここにあります)。保護が常に存在するとは限らない場合、これはあまり便利な機能ではありません。

何かを「アサート」している場合は、if/throw意味のある例外ブロックを作成することもできます。この思考プロセスは、すべての値が境界内にあることを確認するためにメソッドの最初に配置された多くのアサートから来ています。前提条件をテストすることは、期待される事後条件を持つための非常に重要な部分です。

単体テストは、作成および保守が必要な追加のコードです。多くの人にとって、これは欠点です。ただし、単体テストフレームワークの現在のクロップがあるので、比較的少ないコードで多数のテスト条件を生成できます。パラメータ化されたテストと「理論」は、多数のデータサンプルを使用して同じテストを実行し、見つけにくいバグを発見することができます。

個人的には、アサートを散布するよりも単体テストの方がマイレージが上がることがわかりますが、これは、ほとんどの場合に開発したプラットフォーム(Java/C#)が原因です。他の言語では、より堅牢なアサーションサポートが提供され、さらに保証を提供するために「Design by Contract」(以下を参照)も提供されます。これらの言語のいずれかを使用している場合、単体テストよりもDBCを使用する可能性があります。

http://en.wikipedia.org/wiki/Design_by_contract#Languages_with_native_support

5
Berin Loritsch