実装の詳細に結合しない単体テストの動作


14

彼の講演TDDでは、それがすべてうまくいかなかったので、Ian CooperはTDDのユニットテストの背後にあるケントベックの当初の意図をプッシュし(特にクラスのメソッドではなく、動作をテストするため)、テストを実装に結合しないように主張しています。

save X to some data source典型的なサービスとリポジトリのセットを備えたシステムのような動作の場合、テストを実装の詳細に結合することなく、特定のメソッドの呼び出しなど、リポジトリを介してサービスレベルで一部のデータの保存を単体テストする方法)?この種のカップリングを避けることは、実際には努力する価値がない/何らかの形で悪いですか?


1
データがリポジトリに保存されたことをテストする場合、テストでは実際にリポジトリにアクセスしてデータが存在するかどうかを確認する必要がありますよね?それとも何か不足していますか?

私の質問は、テストをリポジトリの特定のメソッドの呼び出しのような実装の詳細に結合することを避けること、または実際にそれを行う必要があるかどうかについてでした。
アンディハント14年

回答:


8

あなたの具体的な例は、ので、あなたは通常、特定のメソッドが呼び出されたかをチェックすることでテストする必要がある場合であるsaving X to data source手段が通信して外部依存関係、あなたがテストを持って行動がということですので、期待通りに通信が発生しています

ただし、これは悪いことではありません。アプリケーションとその外部依存関係との間の境界インターフェース実装の詳細ではなく、実際、システムのアーキテクチャで定義されています。これは、そのような境界が変更される可能性が低いことを意味します(または、変更する必要がある場合、変更の頻度が最も低くなります)。したがって、テストをrepositoryインターフェイスに結合することで問題が発生することはあまりありません(問題が発生した場合、インターフェイスがアプリケーションから責任を奪っていないかどうかを検討してください)。

次に、UI、データベース、その他の外部サービスから切り離された、アプリケーションのビジネスルールのみを考慮します。これは、コードの構造と動作の両方を自由に変更する必要がある場合です。これは、テストと実装の詳細を結合することで、アプリケーションの全体的な動作に変化がない場合でも、本番コードよりも多くのテストコードを変更することを余儀なくされる場所です。これは、テストではStateなくテストのInteraction高速化に役立つ場所です。

PS:州または相互作用によるテストがTDDの唯一の真の方法であるかどうかを言うつもりはありません-適切な仕事に適切なツールを使用することの問題だと思います。


「外部の依存関係と通信する」という場合、外部の依存関係は、テスト対象のユニットの外部にあるものですか、それともシステム全体にあるものですか?
アンディハント14年

「外部依存関係」とは、アプリケーションのプラグインとみなすことができるものを意味します。アプリケーションとは、永続性やUIに使用するフレームワークなど、あらゆる種類の詳細にとらわれないビジネスルールを意味します。この話のように、ボブおじさんがもっとうまく説明できると思います:youtube.com/watch
v

これは、「機能」または「動作」ベースでテストするための理想的なアプローチであり、機能または動作ごとに1つのテスト(または1つの順列、すなわちさまざまなパラメータ)をテストするのに最適だと思います。ただし、TDDを実行するために機能の「ハッピー」テストが1つある場合、その機能に対して1つの巨大なコミット(およびコードレビュー)が行われることを意味します。これは悪い考えです。これはどのように回避されますか?その機能の一部をテストおよびそれに関連付けられたすべてのコードとして記述し、その後のコミットで機能の残りを増分的に追加しますか?
ヨルダン

実装に結合しているテストの実世界の例を見てみたいです。
PositiveGuy

7

その話の私の解釈は:

  • クラスではなくテストコンポーネント。
  • インターフェイスポートを介してコンポーネントをテストします。

講演では述べられていませんが、アドバイスの想定されるコンテキストは次のようなものだと思います。

  • たとえば、ユーティリティライブラリやフレームワークではなく、ユーザー向けのシステムを開発しています。
  • テストの目標は、競争力のある予算内で可能な限り成功することです。
  • コンポーネントは、C#/ Javaのような単一の成熟した、おそらく静的に型付けされた言語で記述されます。
  • コンポーネントは10000〜50000行のオーダーです。MavenまたはVSプロジェクト、OSGIプラグインなど
  • コンポーネントは、単一の開発者または密接に統合されたチームによって作成されます。
  • あなたは六角形のアーキテクチャのようなものの用語とアプローチに従っています
  • コンポーネントポートは、ローカル言語を残す場所であり、そのタイプシステムは、http / SQL / XML / bytes / ...に切り替えます。
  • すべてのポートをラップすることは、Java / C#の意味での型付きインターフェースであり、実装をスイッチテクノロジーに切り替えることができます。

したがって、コンポーネントのテストは可能な限り最大の範囲であり、その範囲内で何かを単体テストと呼ぶことができます。これは、一部の人々、特に学者がこの用語を使用する方法とはかなり異なります。典型的な単体テストツールチュートリアルの例とはまったく異なります。ただし、ハードウェアテストの起源と一致しています。ボードとモジュールは単体でテストされており、ワイヤとネジではありません。または、少なくとも、ネジをテストするための模擬ボーイングを構築しないでください...

それから外挿して、私自身の考えをいくつか入れて、

  • すべてのインターフェイスは、入力、出力、または共同作業者(データベースなど)になります。
  • 入力インターフェイスをテストします。メソッドを呼び出し、戻り値をアサートします。
  • 出力インターフェイスをモックします。所定のテストケースで予想されるメソッドが呼び出されることを確認します。
  • あなたは協力者を偽造します。シンプルだが機能する実装を提供する

それを適切かつきれいに行えば、モックツールはほとんど必要ありません。システムごとに数回しか使用されません。

通常、データベースは共同編集者であるため、偽造されるのではなく偽造されます。これを手作業で実装するのは苦痛です。幸いなことに、そのようなものはすでに存在しています

基本的なテストパターンは、一連の操作を実行することです(たとえば、ドキュメントの保存と再読み込み)。動作確認します。これは、他のテストシナリオと同じです。そのようなテストが失敗する原因となる可能性がある(動作中の)実装の変更はありません。

例外は、データベースレコードが書き込まれますが、テスト中のシステムによって読み取られないことです。たとえば、監査ログなど。これらは出力であるため、モックする必要があります。テストパターンは、一連の操作を行います。指定されたメソッドと引数を使用して、監査インターフェイスが呼び出されたことを確認します。

ここでも、mockitoなどのタイプセーフなモッキングツールを使用している場合、インターフェイスメソッドの名前を変更してもテストエラーは発生しません。ロードされたテストでIDEを使用する場合、メソッドの名前変更とともにリファクタリングされます。しないと、テストはコンパイルされません。


インターフェイスポートの具体的な例を説明してください。
PositiveGuy

出力インターフェイスの例は何ですか。コードを具体化できますか?入力インターフェイスと同じです。
PositiveGuy

(Java / C#の意味での)インターフェイスはポートをラップします。これは、外部の世界(d / b、ソケット、httpなど)と通信するものであれば何でもかまいません。出力インターフェイスとは、ポートを介して外部から送られてくる戻り値を持つメソッドを持たないインターフェイスで、例外または同等のもののみです。
-soru

入力インターフェースは反対であり、協力者は入力と出力の両方です。
-soru

1
ビデオで説明されているものとはまったく異なる設計アプローチと一連の用語について話していると思います。しかし、90%の時間、リポジトリ(つまりデータベース)は共同作業者であり、入力でも出力でもありません。したがって、それに対するインターフェースはコラボレーションインターフェースです。
soru

0

私の提案は、状態ベースのテストアプローチを使用することです。

GIVEN 既知の状態のテストDBがあります

WHEN サービスは、引数Xと呼ばれています

THEN アサートDBが読み取り専用のリポジトリメソッドを呼び出し、その戻り値をチェックすることによって、期待される状態に元の状態から変更されていること

そうすることで、サービスの内部アルゴリズムに依存せず、テストを変更せずに実装を自由にリファクタリングできます。

ここでの唯一の結合は、DBからデータを読み取るために必要なサービスメソッド呼び出しとリポジトリ呼び出しです。これは問題ありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.