6

コンストラクターとパブリック関数/メソッドのすべてのパラメーターを検証するための規則があります。参照型の必須パラメーターについては、主にnull以外をチェックします。これが、型の必須依存関係を設定するコンストラクターでの主な検証です。

これを行う最大の理由は、エラーを早期にキャッチし、障害のあるパラメーターがいつどこに導入されたかを知らずに、数時間後にnull参照例外を取得しないようにするためです。ますます多くのTDDに移行し始めると、一部のチームメンバーは検証が冗長であると感じます。

TDDのボーカルアドボケイトであるボブおじさんは、パラメータの検証を行うことに対して強くアドバイスします。彼の主な議論は、「すべてが機能することを確認する一連の単体テストがある」ということのようです。

しかし、私はそれの存続期間中、単体テストが開発者が本番コードの悪いパラメーターでこれらのメソッドを呼び出すのをどのように防ぐことができるかを知ることができません。

ユニットテスターの皆さん、具体的な例を使って合理的な方法でこれを説明していただければ、このパラメーターの検証を喜んで承ります。

4

5 に答える 5

7

私の答えは「できません」です。基本的に、私はこれについてボブおじさんに同意しないようです(とりわけ)。

ライブラリコードでnull以外の引数を単体テストし、呼び出し元のコードで、気付かないうちにライブラリにnull引数を提供するパスを単体テストした状況を想像するのは簡単です。それだけでなく、その特定のパスに問題が発生することもありません100%のカバレッジと実際にはかなり良い一連のテストを行うことができますが、それでも問題に気付くことはありません。

すべて大丈夫ですか?いいえ、もちろんそうではありません。あなたが図書館の契約に気づかずに違反しているからです(null以外の値を教えてはいけません)。null引数を提供しているのは、それが問題にならない状況だけであることに満足できますか?私はそうは思いません-特にあなたが議論がnullであることにさえ気づいていなかったなら。

私の見解では、パブリックAPIは、呼び出し元のコードとAPI自体が単体テストされているかどうかに関係なく、引数を検証する必要があります。コードを呼び出す際の問題を明らかにし、できるだけ早く明らかにする必要があります。

于 2012-06-24T21:18:44.290 に答える
3

それは私が何年もの間自分自身に問いかけてきた質問ですが、それでも満足のいく答えは得られていません。

しかし、引数の検証に関しては、次の2つのケースを区別する必要があると思います。

  1. 論理プログラミングエラーをキャッチするために引数を検証していますか?

    if (foo == null) throw new ArgumentNullException("foo");
    

    その一例である可能性が非常に高いです。

  2. 引数が無効である可能性があり、拒否する必要がある外部入力(ユーザーによって提供されるか、構成ファイルまたはデータベースから読み取られる)であるため、引数を検証していますか?

    if (customerDateOfBirth == new DateTime(1900, 1, 1)) throw …;
    

    このタイプの引数チェックの可能性があります。

(チーム外の誰かが使用するAPIを公開している場合は、ポイント2も大まかに当てはまります。)

単体テスト、契約による設計、およびある程度「早期に失敗する」などの方法論は、主に最初のタイプの引数検証に焦点を合わせていると思います。つまり、無効な入力ではなく、論理プログラミングエラーを検出しようとします。

その場合は、どのエラー検出方法を使用するかは実際には問題ではないと思います。それぞれに長所と短所があります。極端な場合(たとえば、バグのないコードを作成する能力に絶対的な信頼がある場合)、これらのチェックを完全に削除することもできます。

ただし、コード内の論理エラーを検出するために選択する方法が何であれ、ユーザー入力などを検証する必要があるため、2種類の引数チェックを区別する必要があります。


†)契約による設計、単体テスト、および「早期失敗」の相対的な長所と短所を比較するアマチュアの不完全な試み:

(あなたはそれを求めていませんでしたが...私はいくつかの重要な違いに言及します。)

早期に失敗する(例:メソッドの開始時に明示的な引数の検証):

  • ガードなどの基本的なチェックnullを書くのは簡単です
  • 同じ構文での論理エラーと外部入力の検証に対するガードを混同する可能性があります
  • メソッドの相互作用をテストすることはできません
  • メソッドのコントラクトを厳密に定義する(したがって考える)ことを推奨しません

ユニットテスト:

  • 実際のアプリケーションを実行せずにコードを分離してテストできるため、バグの検出が迅速になります
  • 論理エラーが発生した場合、各単体テストはコードの特定の「ユースケース」を表すため、原因を見つけるためにスタックをトレースする必要はありません。
  • 複数のオブジェクト間の相互作用など、単一のメソッド以上のものをテストできます(スタブとモックを考えてください)
  • 簡単なテスト(ガードなどnull)を作成することは、「早期に失敗する」アプローチよりも手間がかかります(Arrange-Act-Assertパターンに厳密に準拠している場合)。

契約による設計:

  • クラスのコントラクトを明示的に指定するように強制します(ただし、これは単体テストでも可能です—まったく別の方法で)
  • クラスの不変条件(常に真でなければならない内部条件)を簡単に述べることができます
  • 他のアプローチほど多くのプログラミング言語/フレームワークでサポートされていません
于 2012-06-24T21:12:31.450 に答える
2

それはすべて、開発しているアプリケーションのタイプによって異なります。

  1. 私はほとんどの時間をパブリックAPIを公開しないアプリケーションの作成に費やしてきました。この場合、アプリケーションは、すべてのパラメーターがnullとは異なる必要があり、異なるという意味で決定論的である必要があります。簡単に言うと、これらの無効な入力がアプリケーションに侵入してnull参照などになることがないように、システム境界で入力検証を実行する必要があります。この種のアプリケーションでは、アプリケーションを取得する場所でアプリケーションの入力をチェックすることを完全に制御できます。

  2. パブリックAPIを作成している場合は、null参照をチェックしないことをお勧めします。例外をスローする可能性のあるすべてのMSDNクラスメソッドを見てください。これらはすべて、前提条件チェックとしてAPI内で発生します。詳細については、C#フレームワークの設計ガイドラインを参照してください。

私の意見では、公開されている(またはされていない)APIアプリケーションであっても、メソッドの前提条件を設定することは常に良いことです(これらのコントラクトは、将来コードを処理するピア向けのドキュメントです)。

于 2012-06-24T21:20:34.360 に答える
2

私はほとんどすべてについてボブおじさんに同意しますが、これはこれではありません。私は「早く失敗し、激しく失敗する」という方針に投票します。

于 2012-06-25T13:30:49.983 に答える
0

これはTDDとは何の関係もありません。

パブリックAPIの場合、はい、可能な限り高速に引数チェックを実行する必要があります。

コンストラクターの引数チェックはすべて、チーム外の他の人によって消費されないため、私にはまったく不要に思えます。なぜヌルチェックがあったのですか?これらのメソッドを呼び出しているコードには信頼がありませんでした。

では、パブリックAPIとは何ですか?すべてのパブリックメソッド?もしそうなら、内部APIと呼ばれるようなものはないと思います。では、なぜpublicという単語を使用するのでしょうか。すべてのパブリックメソッドがnull/境界チェックを実行する必要があると言うのはなぜですか。

問題の根本的な原因は、私たち自身のコードとチームメンバーへの信頼の欠如であると思います。明らかに、私たちは間違った方法で問題を解決しています。

于 2012-06-27T15:12:20.647 に答える