私は本当にユニットテストとTDDに恋をしました-私はテストに感染しています。
ただし、ユニットテストは通常、パブリックメソッドに使用されます。時には、私はいくつかの仮定-アサーションもプライベートメソッドでテストする必要がありますが、それらのいくつかは「危険」であり、リファクタリングはそれ以上の助けにはならないためです。 (私は知っています、テストフレームワークはプライベートメソッドのテストを許可します)。
そのため、プライベートメソッドの最初と最後の行が両方ともアサーションであることが私の習慣になりました。
しかし、私は「確実にするため」にパブリックメソッド(およびプライベートメソッド)でアサーションを使用する傾向があることに気付きました。パブリックメソッドの仮定はユニットテストフレームワークによって外部からテストされるため、これは「テストの重複」である可能性がありますか?
誰かがあまりにも多くのアサーションをコードのにおいとして考えることができますか?
これらのアサーションは、仮定をテストするのに非常に役立ちますが、ドキュメントの別の非常に重要な目的にも役立ちます。パブリックメソッドの任意のリーダーは、テストスイートを見なくても、アサートを読み取って事前条件と事後条件をすばやく判断できます。このため、テストの理由ではなく、文書化の理由でこれらのアサートを保持することをお勧めします。技術的にはアサーションを複製していますが、これらは2つの異なる目的を果たし、両方で非常に役立ちます。
アサーションとしてそれらを維持することは、単にコメントを使用するよりも優れています。なぜなら、それらは実行されるたびに仮定を積極的にチェックするからです。
手作業で Design-by-Contract を実行しようとしているようです。
DbCを行うことは良いアイデアですが、少なくともネイティブでサポートする言語( Eiffel など)への切り替えを検討するか、少なくともプラットフォームの契約フレームワーク(例 .NETのMicrosoftコードコントラクト は非常に優れており、おそらく最も洗練されたコントラクトフレームワークであり、Eiffelよりも強力です)。そうすれば、契約の力をより効果的に活用できます。例えば。既存のフレームワークを使用して、ドキュメントまたはIDE(たとえば、.NETのコードコントラクトはIntelliSenseに表示され、VS Ultimateを使用している場合は、入力中のコンパイル時の自動定理証明。同様に、多くのJava契約フレームワークには、契約をJavaDoc APIドキュメントに自動的に抽出するJavaDocドックレットがあります)。
そして、あなたの状況でそれを手動で行う以外に方法がないことがわかったとしても、少なくともあなたはそれが何と呼ばれるかを知っており、それについて読むことができます。
つまり、簡単に言うと、実際にDbCを実行している場合、それを知らなくても、これらのアサーションは完全に適切です。
入力パラメーターを完全に制御できない場合は常に、単純なエラーがないか事前にテストすることをお勧めします。たとえばnullで失敗します。
これは、コードが適切に失敗することをテストするgiven不正な入力パラメーターをテストしてからthatを文書化する必要があるため、テストの重複ではありません。
私はあなたが戻り値のパラメータをアサートするべきではないと思います(読者に理解してほしい不変条件が明示的にない限り)。これはユニットテストの仕事でもあります。
個人的には、Javaのassert
statementを無効にすることができるため、誤ったセキュリティになるため、私は好きではありません。
パブリックメソッドでアサーションを使用することはさらに重要だと思います。入力を制御できず、仮定が崩れる可能性が高くなるからです。
入力条件のテストは、すべてのパブリックメソッドおよび保護されたメソッドで行う必要があります。入力がプライベートメソッドに直接渡される場合、その入力のテストは冗長になる可能性があります。
出力条件(オブジェクトの状態や戻り値!= nullなど)のテストは、内部メソッド(プライベートメソッドなど)で行う必要があります。出力がプライベートメソッドからパブリックメソッドの出力に、追加の計算や内部状態の変更なしに直接渡される場合、パブリックメソッドの出力条件のテストは冗長になる可能性があります。
ただし、冗長性によって可読性が向上し、保守性も向上する可能性があることに直接同意します(直接割り当てまたは返却が将来的になくなる場合)。
アサーションと「適切な」エラー/例外処理の実装方法の詳細が答えに関係するため、この問題について言語にとらわれないことは困難です。 Java&C++に関する私の知識に基づいて、これが私の$ 0.02です。
プライベートメソッドのアサーションは、行き過ぎてそれらを配置しないことを前提として、良いことですeverywhere。それらを本当に単純なメソッドに配置したり、不変フィールドのようなものを繰り返しチェックしたりしている場合は、コードが不必要に雑然としていることになります。
一般に、パブリックメソッドでのアサーションは回避するのが最善です。メソッドコントラクトに違反していないことを確認するなどの処理を行う必要がありますが、違反している場合は、意味のあるメッセージを含む適切に型指定された例外をスローし、可能な場合は状態を元に戻します(@rwongが「完全な適切なエラー処理」)。
一般的に言えば、アサーションはyour development/debuggingを支援するためにのみ使用する必要があります。コードを使用するときに、APIを使用する誰もがアサーションを有効にすることさえできるとは限りません。それらはコードのドキュメント化に役立つ場合もありますが、同じものをドキュメント化するためのより良い方法がしばしばあります(つまり、メソッドのドキュメント化、例外)。
(主に)例外的な回答のリストに追加すること、多くのアサート、および重複のもう1つの理由は、クラスがその存続期間にわたっていつ、誰によってどのように変更されるかがわからないことです。あなたが1年で死んでしまう使い捨てのコードを書いているなら、心配は無用です。 20年以上使用されるコードを書いている場合、コードはかなり異なって見えます-そして、あなたがした仮定はもはや有効ではないかもしれません。その変更を行う人は主張をありがとうございます。
また、すべてのプログラマーが完璧なわけではありません。ここでも、これらの「スリップアップ」の1つがあまり伝播しないことを表明しています。
これらの表明(および仮定に関する他のチェック)が保守のコストを削減する効果を過小評価しないでください。
アサーションを複製すること自体は間違っていませんが、「確認のためだけ」にアサートを作成することは最善ではありません。これは、各テストが達成しようとしていることを正確にまとめたものです。絶対に必要なものだけを主張してください。テストがMoqを使用してメソッドが呼び出されたことを確認する場合、そのメソッドが呼び出された後に何が起こるかは問題ではなく、テストはメソッドが呼び出されることを確認することのみに関係します。
1つまたは2つを除いて、すべての単体テストに同じアサートのセットがある場合、少しリファクタリングすると、リファクタリングの実際の影響を示すのではなく、まったく同じ理由ですべてのテストが失敗する可能性があります。すべてのテストを実行すると、すべてのテストが失敗し、すべてのテストに同じアサートがあるため、その失敗を修正し、テストを再度実行し、別の理由で失敗し、その失敗を修正します。完了するまで繰り返します。
また、テストプロジェクトのメンテナンスも検討してください。新しいパラメーターを追加したり、出力を少しずつ微調整したりすると、一連のテストに戻ってアサートを変更するか、アサートを追加する必要がありますか?また、コードによっては、すべてのアサートがパスすることを確認するためだけに、さらに多くの設定が必要になる場合があります。
ユニットテストに重複したアサーションを含めたいという魅力を理解できますが、それは実際にはやりすぎです。私は最初のテストプロジェクトで同じ道を進みました。メンテナンスだけで気が狂ったので、私はそれから離れました。
ただし、ユニットテストはパブリックメソッドに使用されます。
テストフレームワークでアクセサーが許可されている場合は、プライベートメソッドのユニットテストも推奨されます(IMO)。
誰かがあまりにも多くのアサーションをコードのにおいとして考えることができますか?
私がやります。アサーションは問題ありませんが、最初の目標はシナリオは不可能です;それが起こらないというだけではありません。