TDDの観点から、モックではなくライブエンドポイントに対してテストする場合、私は悪い人ですか?


16

私はTDDを宗教的に守ります。私のプロジェクトは通常、テストカバレッジが85%以上であり、意味のあるテストケースがあります。

私はHBaseで多くの作業を行っていますが、メインのクライアントインターフェイスであるHTableはモックの苦痛です。ライブエンドポイントを使用するテストを記述するよりも、ユニットテストを記述するのに3〜4倍時間がかかります。

哲学的に、モックを使用するテストは、ライブエンドポイントを使用するテストよりも優先されるべきであることを知っています。しかし、HTableのモックは深刻な痛みであり、実際のHBaseインスタンスに対するテストよりも多くの利点があるとは確信していません。

私のチームの全員がワークステーションでシングルノードHBaseインスタンスを実行し、JenkinsボックスでシングルノードHBaseインスタンスを実行しているため、可用性の問題はありません。ライブエンドポイントテストは、モックを使用するテストよりも明らかに実行に時間がかかりますが、実際には気にしません。

現在、すべてのクラスに対してライブエンドポイントテストと模擬ベースのテストを作成しています。モックを捨てたいのですが、結果として品質が低下するのは望ましくありません。

皆さんはどう思いますか?


8
ライブエンドポイントは、実際には単体テストではありませんか?これは統合テストです。しかし、最終的にはおそらく実用主義の問題でしょう。モックの作成に時間を費やすか、機能の作成やバグの修正に時間を費やすことができます。
ロバートハーヴェイ14年

4
独自のコードに対して単体テストを実行することでサードパーティのサービスを停止するという話を聞いたことがありますが、それはライブエンドポイントに接続されていました。レート制限は、単体テストで通常行われたり気にかけられるものではありません。

14
あなたは悪い人ではありません。あなたは悪いことをしている善良な人です。
キラレッサ14年

14
私はTDDを宗教に従っています この方法論のどれもが真剣に受け止められるものはないと思います。;)
FrustratedWithFormsDesigner 14年

9
宗教的にTDDに従うことは、カバーされていない15%のコードを破棄することを意味します。
mouviciel 14年

回答:


23
  • 私の最初の推薦はあなたが所有しないタイプをモックにしないことです。HTableはモックの本当の苦痛であると述べました-代わりに、必要なHTableの機能の20%を公開するアダプターでラップし、必要に応じてラッパーをモックする必要があります。

  • そうは言っても、皆さんが所有している型について話していると仮定しましょう。モックベースのテストが、すべてがスムーズに進むハッピーパスシナリオに焦点を合わせている場合、統合テストはおそらくまったく同じパスを既にテストしているため、それらを捨てる何かを失うことはありません。

    ただし、テスト対象のシステムが、実際の具体的なオブジェクトに関係なく、コラボレーターの契約で定義されているように発生する可能性のあるあらゆる小さな事柄にどのように反応するかについて考え始めると、分離テストは興味深いものになります。これは、一部の人々が基本的な正確性と呼ぶものの一部です。これらの小さなケースの多くがあり、それらのより多くの組み合わせがあります。ここで、統合テストはお粗末になり始めますが、隔離されたテストは高速で管理しやすくなります。

    具体的には、HTableアダプターのメソッドの1つが空のリストを返すとどうなりますか?nullを返すとどうなりますか?接続例外をスローするとどうなりますか?これらのいずれかが発生する可能性がある場合、アダプターの契約定義する必要があり、そのコンシューマーはこれらの状況に対処する準備をする必要があります。したがって、それらのテストの必要性があります。

要約すると、統合テストとまったく同じことをテストした場合、モックベースのテストを削除しても品質が低下することはありません。ただし、追加の分離テスト(および契約テスト)を想像しようとすると、統合テストで考えるのが困難であった、および/またはテストが遅かった欠陥に取り組むことで、インターフェイス/契約を広範囲に考え、品質を向上させることができます。


+1模擬テストを使用してエッジケースを作成する方が、データベースにこれらのケースを追加するよりもはるかに簡単です。
ロブ14年

あなたの答えのほとんどに同意します。ただし、アダプター部分について同意するかどうかはわかりません。HTableはかなりベアメタルなので、モックするのは苦痛です。たとえば、バッチ取得操作を実行する場合は、多数のGetオブジェクトを作成し、それらをリストに追加してから、HTable.batch()を呼び出す必要があります。モックの観点からすると、これは深刻な痛みです。HTable.batch()に渡すgetオブジェクトのリストを調べて、そのget()オブジェクトのリストに対して正しい結果を返すカスタムMatcherを作成する必要があるためです。深刻な痛み。
サンフロイド14年

私はHTable用の素敵でフレンドリーなラッパークラスを作成して、そのすべてのハウスキーピングを処理できると思いますが、その時点で...私はちょっとHTableの周りにフレームワークを構築しているように感じます、それは本当に私の仕事ですか?通常、「フレームワークを構築しましょう!」私が間違った方向に進んでいる兆候です。HBaseをより親しみやすくするためにクラスを書くのに何日も費やすことができましたが、それが私の時間の素晴らしい使い方かどうかはわかりません。さらに、単純な古いHTableオブジェクトの代わりに、インターフェイスまたはラッパーを見て回っています。これにより、コードがより複雑になります。
サンフロイド14年

しかし、私はあなたの主要な点に同意します、彼らが所有していないクラスのためにモックを書くべきではないということです。また、統合テストと同じことをテストする模擬テストを作成しても意味がないことは間違いありません。インターフェイス/契約のテストにはモックが最適であると思われます。アドバイスをありがとう-これは私を大いに助けてくれました!
サンフロイド14年

HTableが実際に何をするのか、どのように使用するのかはほとんどわからないので、手紙にラッパーの例を使用しないでください。ラップするものが比較的小さいと思ったため、ラッパー/アダプターについて言及しました。HTableに1対1のレプリカを導入する必要はありません。もちろん、フレームワーク全体は言うまでもなく苦痛になりますが、アプリケーションの領域とHTableの領域の間のインターフェースであるseamが必要です。HTableの機能の一部をアプリケーション独自の用語に言い換える必要があります。リポジトリパターンは、データアクセスに関してはこのような継ぎ目の完璧な化身です。
guillaume31

11

哲学的には、モックを使用するテストは、ライブエンドポイントを使用するテストよりも優先されるべきです。

少なくとも、TDD支持者の間で現在 進行中の論争のポイントだと思います。

私の個人的な見方は、モックベースのテストはほとんどの場合、インターフェイスコントラクトの形式を表現する方法であるということです。理想的には、インターフェースを変更した場合にのみ、破損(つまり、失敗)します。そのため、Javaなどのかなり強く型付けされた言語では、明示的に定義されたインターフェイスを使用する場合、ほぼ完全に不要です。コンパイラは、インターフェイスを変更したかどうかを既に通知しています。

主な例外は、おそらく注釈やリフレクションに基づいた非常に汎用的なインターフェイスを使用している場合であり、コンパイラが自動的に有効にポリシングできないことです。それでも、モックを使用して手動で行うのではなく、プログラムで検証を実行する方法(SQL構文チェックライブラリと同等)があるかどうかを確認する必要があります。

「ライブ」ローカルデータベースでテストする場合は、後者の場合です。htableの実装が開始され、手作業で書き出すと考えるよりもはるかに包括的なinterfacve契約の検証が適用されます。

残念ながら、模擬ベースのテストのはるかに一般的な使用法は、次のテストです。

  • テストが作成された時点のコードが何であれ合格
  • 存在することと実行の種類以外のコードのプロパティに関する保証を提供しません
  • そのコードを変更するたびに失敗する

もちろん、このようなテストはすぐに削除する必要があります。


1
これを十分にバックアップすることはできません。フィラーの100%カバレッジよりも、1%の優れたテストでカバレッジを取得したいです。
イアン14年

3
モックベースのテストは、実際に2つのオブジェクトが互いに通信するために使用するコントラクトを記述しますが、Javaのような言語の型システムが実行できるものをはるかに超えています。メソッドシグネチャだけでなく、引数または返される結果の有効な値の範囲、どの例外を受け入れるか、メソッドを呼び出すことができる順序および回数などを指定することもできます。それらの変化です。その意味で、私は彼らがまったく不必要だとは思わない。モックベースのテストの詳細については、infoq.com / presentations / integration -tests- scamを参照してください。
guillaume31 14年

1
...つまりは、インタフェースの呼び出しの周りにロジックをテスト同意
ロブ・

1
インターフェースの静的型付けを少なくするもののリストに、未チェックの例外、未宣言の前提条件、および暗黙の状態を確実に追加できるため、単純なコンパイルの代わりにモックベースのテストを正当化できます。ただし、問題は、これらの側面変更されると、その仕様が暗黙的であり、すべてのクライアントのテストに分散されることです。更新されない可能性が高いため、緑色のチェックマークの後ろに静かにバグを隠してください。
soru 14年

「それらの仕様は暗黙的です」:インターフェースの契約テスト(blog.thecodewhisperer.com/2011/07/07/contract-tests-an-example)を作成し、モックをセットアップするときにそれらに固執する場合ではありません。
guillaume31

5

エンドポイントベースのテストの実行には、モックベースのテストよりどれくらい時間がかかりますか?かなり長い場合は、はい、テストを書く時間を投資するだけの価値があります。これは、ユニットテストを何度も実行する必要があるためです。エンドポイントベースのテストが「純粋な」単体テストではない場合でも、それほど長くない場合は、ユニットのテストがうまく機能している限り、それについて信心する理由はありません。


4

私はguillaume31の反応に完全に同意します。あなたが所有していないタイプをモックすることはありません!

通常、テストの痛み(複雑なインターフェイスのモック)は、設計の問題を反映しています。おそらく、モデルとデータアクセスコードの間に抽象化が必要です。このような問題を解決する最も一般的な方法は、六角形のアーキテクチャとリポジトリパターンを使用した例です。

統合テストを行うために統合テストを実行する場合、統合テストを実行する場合、ロジックをテストしているためユニットテストを実行する場合は、単体テストを実行し、永続性を分離します。しかし、外部システムからロジックを分離する方法がわからないため(またはその痛みを分離するため)統合テストを実行するのは大きな悪臭であり、実際の必要性ではなく、設計上の制限のためにユニットよりも統合を選択しています統合をテストします。

このトークフォームイアン・クーパーに見てみましょう:http://vimeo.com/68375232、彼は六角形のアーキテクチャとテスト語る、彼は本当にインスピレーションを得た話本当のTDDについてのあなたのよう解き多くの質問こと、いつ、どのような模擬する語ります。


1

TL; DR-私の見方では、テストにどれだけの労力を費やしたか、そして実際のシステムにもっと多くを費やす方が良かったかどうかによって異なります。

ロングバージョン:

ここにいくつかの良い答えがありますが、私の見解は異なります:テストは経済活動であり、それ自体に見返りが必要です。そして、あなたが費やす時間が開発とシステムの信頼性(またはあなたが外に出そうとする他のもの)に返されない場合テストの場合)あなたは悪い投資をしている可能性があります。あなたはテストを書くのではなく、システムを構築するビジネスをしています。したがって、テストの記述と保守の労力を削減することが重要です。

たとえば、テストから得た主な値は次のとおりです。

  • 信頼性(したがって開発速度):コードをリファクタリングする/新しいフレームワークを統合する/コンポーネント/ポートを別のプラットフォームにスワップする、まだ機能していることを確信する
  • 設計フィードバック:低/中レベルのインターフェイスに関する古典的なTDD / BDD「コードを使用」フィードバック

ライブエンドポイントに対するテストでは、これらが引き続き提供されます。

ライブエンドポイントに対するテストのいくつかの欠点:

  • 環境設定-テスト実行環境の設定と標準化はより手間がかかり、微妙に異なる環境設定により微妙に異なる動作が発生する可能性があります
  • ステートレス性-ライブエンドポイントに対して作業すると、エンドポイントの状態が変化することに依存するテストの作成が促進される可能性があります。
  • テスト実行環境は脆弱です-テストが失敗した場合、それはテスト、コード、またはライブエンドポイントですか?
  • 実行速度-ライブエンドポイントは通常より遅く、時には並列化が難しい
  • テスト用のエッジケースの作成-通常、モックでは些細なことですが、ライブエンドポイントで苦痛を感じることがあります(たとえば、設定が難しいものはトランスポート/ HTTPエラーです)

私がこの状況にありエンドポイントのモックがテストの作成をかなり遅くしたのに対して、欠点は問題に思えなかった場合、ハートビートでライブエンドポイントに対してテストする必要があります。しばらくしてからもう一度確認して、実際には欠点が問題にならないことを確認してください。


1

テストの観点から、絶対に必要な要件がいくつかあります。

  • テスト(ユニットまたはそれ以外)は、実稼働データに触れる方法を持たないようにする必要があります
  • あるテストの結果が別のテストの結果に影響してはいけません
  • 常に既知の位置から開始する必要があります

テスト外で状態を維持するソースに接続する場合、これは大きな課題です。「純粋な」TDDではありませんが、Ruby on Railsのクルーは、この問題をあなたの目的に適応できる方法で解決しました。Railsテストフレームワークは次のように機能しました。

  • 単体テストの実行時にテスト構成が自動的に選択された
  • データベースは、単体テストの実行開始時に作成および初期化されました
  • データベースは単体テストの実行後に削除されました
  • SqlLiteを使用する場合、テスト構成はRAMデータベースを使用しました

この作業はすべてテストハーネスに組み込まれており、適切に機能します。それにはもっと多くのことがありますが、この会話には基本で十分です。

私が長い時間をかけて取り組んできたさまざまなチームでは、たとえそれが最も正しいパスでなかったとしても、コードのテスト促進する選択をします。理想的には、データストアへのすべての呼び出しを制御したコードでラップします。理論的には、これらの古いプロジェクトのいずれかが新しい資金を得た場合、ほんの一握りのクラスに注意を集中することにより、データベースにバインドされていたものから、Hadoopにバインドされたものに戻すことができます。

重要な側面は、実稼働データを台無しにすることではなく、テストしていると思われるものを本当にテストしていることを確認することです。コードからであっても、外部サービスをオンデマンドで既知のベースラインにリセットできることが非常に重要です。

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