防御的なプログラミングをサポートおよび保証するためのテスト
防御的プログラミングは、実行時にシステムの整合性を保護します。
テストは(ほとんど静的な)診断ツールです。実行時には、テストはどこにも見えません。それらは、高いレンガの壁や岩のドームを設置するために使用される足場のようなものです。構造中に重要な部分を残さないでください。建設中に足場がそれを支えているからです。建設中に足場を支えて、すべての重要な部品を入れやすくします。
編集:アナロジー
コード内のコメントの類推はどうですか?
コメントには目的がありますが、冗長または有害な場合もあります。たとえば、コードに関する固有の知識をコメントに入れてからコードを変更すると、コメントはせいぜい無意味になり、最悪の場合は有害になります。
MethodAはnullを取得できず、MethodBの引数はである必要があるなど、コードベースの多くの固有の知識をテストに入力するとします> 0
。その後、コードが変更されます。ヌルはAで大丈夫であり、Bは-10までの値を取ることができます。既存のテストは機能的に間違っていますが、引き続き合格します。
はい、コードを更新すると同時にテストを更新する必要があります。また、コードを更新すると同時にコメントを更新(または削除)する必要があります。しかし、私たちは皆、これらのことが常に起こるとは限らないことを知っています。
テストでは、システムの動作を検証します。その実際の動作はシステム自体に固有のものであり、テストに固有のものではありません。
何が間違っている可能性がありますか?
テストに関する目標は、失敗する可能性のあるすべてを考え出し、正しい動作をチェックするテストを作成してから、すべてのテストに合格するようにランタイムコードを作成することです。
つまり、防御的なプログラミングがポイントです。
テストが包括的な場合、TDDは防御的なプログラミングを推進します。
より多くのテスト、より防御的なプログラミングの推進
バグが必然的に見つかると、バグを明示する条件をモデル化するためのテストがさらに作成されます。その後、これらのテストに合格するためのコードでコードが修正され、新しいテストはテストスイートに残ります。
テストの良いセットは、良い引数と悪い引数の両方を関数/メソッドに渡し、一貫した結果を期待します。これは、テストされたコンポーネントが前提条件チェック(防御プログラミング)を使用して、渡された引数を確認することを意味します。
一般的に言えば...
たとえば、特定のプロシージャへのnull引数が無効な場合、少なくとも1つのテストがnullを渡し、何らかの「無効なnull引数」例外/エラーが予期されます。
もちろん、少なくとも1つの他のテストが有効な引数を渡します(または、大きな配列をループして、10個の有効な引数を渡します)。結果の状態が適切であることを確認します。
テストがそのnull引数を渡さず、予期される例外で平手打ちされた場合(およびコードが渡された状態を防御的にチェックしたためにその例外がスローされた場合)、nullはクラスのプロパティに割り当てられるか、埋められる可能性がありますあるべきではないコレクション。
これにより、ソフトウェアが出荷された後の地理的に離れた場所で、クラスインスタンスが渡されるシステムのまったく異なる部分で予期しない動作が発生する場合があります。そして、それは私たちが実際に回避しようとしている種類のものですよね?
さらに悪化する可能性もあります。無効な状態のクラスインスタンスは、後で使用するために再構成された場合にのみ障害を引き起こすために、シリアル化および保存できます。Geez、私は知らない、多分それはシャットダウンの後にそれ自身の永続的な設定状態をデシリアライズできないので再起動できないある種の機械的制御システムだろう。または、クラスインスタンスをシリアル化し、他のエンティティによって作成されたまったく異なるシステムに渡すと、そのシステムがクラッシュする可能性があります。
特に、他のシステムのプログラマーが防御的にコーディングしなかった場合。