web-dev-qa-db-ja.com

TDDで内部をテストしますか?

私はTDDに不慣れであり、ソフトウェア開発一般に比較的新しい(例:4年未満の経験)が、学習しようとしている。

私はTDDをいじっていますが、よくある問題であることに気づきました。私は、必要に応じて統合テストを伴うこともある高レベルのAPIコントラクトと、実装に依存する低レベルのユニットテストの両方をテストしてきました。

私は最近 イアンクーパーによる講演 を見て、内部をテストするべきではなく、プログラムの意図された実際の機能(たとえば、プログラムの契約が何であれ)をテストするべきではないことを示唆しています。ここでは言い換えていますが、メッセージを見逃している可能性があります。

内部を壊すテストを心配せずにリファクタリングできるようにすることは私にとって理にかなっていますが、同時に、達成するのを助けるために私が書く多くの内部関数(プライベートかどうか)をテストしないのは奇妙に感じます手元にあるより大きな機能。さらに、高い契約レベルでのみテストするテストを作成しようとすると、テストが大きくなりすぎたり複雑になったりすることがあります。

また、これは大量の単体テストの主要なプッシュと矛盾していませんか?一般に、単体テストではなく、失敗したときに特定の問題を特定するのに役立つので、単体テストが多いほど良いでしょうか?

これをどのようにバランスさせるのですか?私が経験を通じて時間をかけて学ぶのは、単にバランスをとる行為ですか?

3
Adam Thompson

また、これは大量の単体テストの主要なプッシュと矛盾していませんか?一般に、単体テストではなく、失敗したときに特定の問題を特定するのに役立つので、単体テストが多いほど良いでしょうか?

確かにそうですが、これは必ずしも悪いことではありません。

TDDの主要な利点の1つは、契約を定義するのに役立つだけでなく、「ユニット」として何が意味があるかということです。

特定の機能の特定のレベルのテストを記述し、意味のある動作だと感じたものはすべてカバーします。

上記のマントラにあるように、テストを過剰に実行すると、意味のある説明がまったくないテストが発生するだけでなく、小さな変更ごとにリファクタリングと再編成が非常に困難になるコードが発生します。プロダクションコードは、テストの重大な変更になります。

したがって、内部を内部、つまり、メソッド、クラス、まとまりのあるクラスのクラスタなどにすることができます。どのように定義しますか?あなたはしません。快適な出発点であると感じた場所を見つけ、最初に実行したいことの概要を示すテストを作成します。

次に、流れに進みます。

ユニットが大きくなると、いくつかの抽象化を追加したり、動作を拡張して新しいテストを満たすためにユニットの内部を再編成したりするneedが見つかるかもしれません。大丈夫-どうぞ。いくつかのクラスを追加し、いくつかのインターフェースを抽出します。テストがまだ成功しているために何かが壊れているかどうかがわかります。

そしてifより詳細な範囲の問題があります(たとえば、低レベルのコンポーネントのEdgeケースが原因で、特定の条件下で高レベルのテストが赤くなる)、もちろん先に進むことができますさらに詳細なテストを記述します。テストの範囲は、実装者として気になるものに限定します。

心に留めておくべきことは、テストフィクスチャのエントリポイントは、変更が難しいハード境界ですです。 ではないハード境界であるものはすべてトリビアルに変更されます。

TDDの目的はテストスイートを作成することではないことに注意してください。これはいい副作用です。 TDDの目標は、動作するコードの記述を支援することです。


余談ですが、この種のアプローチは、「教えて、聞かないで」の原則で非常にうまく機能します。出力メカニズムをトップレベルの「ユニット」に渡し、すべてを偽の出力実装に対してアサートします。

VendingMachine vendingMachine = new VendingMachine();

vendingMachine.insertCoins(5);
vendingMachine.displayTo(fakeDisplay);

assertThat(fakeDisplay.toString()).isEqualTo("5");

より複雑な動作を追加しても、テストでVendingMachineインターフェースを操作し、ディスプレイに対してアサートしますが、内部的にはより多くの構成が行われる可能性があります-ディスプレイはより専門的なサブコンポーネントなどに渡される可能性があります等々.

5
Ant P

クラスの内部が非常に複雑で、徹底的なテストが必要な場合は、クラスがSRPに違反しています(過度に実行)。新しい外部ユーティリティクラスのクライアントにそれをリファクタリングし、そのユーティリティクラスを個別にテストします。

3
Kilian Foth