統合テストはすべての単体テストを繰り返すことを意図していますか?


36

関数があるとしましょう(Rubyで書かれていますが、誰でも理解できるはずです):

def am_I_old_enough?(name = 'filip')
   person = Person::API.new(name)
   if person.male?
      return person.age > 21
   else
      return person.age > 18
   end
end

単体テストでは、すべてのシナリオをカバーする4つのテストを作成します。それぞれはPerson::API、スタブ化されたメソッドmale?とでモックされたオブジェクトを使用しますage

次に、統合テストを作成します。Person :: APIはもうm笑されるべきではないと思います。したがって、Person :: APIオブジェクトをモックせずに、まったく同じ4つのテストケースを作成します。あれは正しいですか?

はいの場合、単体テストを書く意味は何ですか、自信を与える統合テストを書くことができたら(スタブやモックではなく、実際のオブジェクトで作業している場合)?


3
まあ、ポイントの1つは、それをモック/ユニットテストすることで、コードの問題を切り分けることができるということです。統合テストが失敗した場合、あなたは誰のコードが壊れているか、あなたのコード、またはAPIについて知らない。
クリスウォーラート

9
テストは4つだけですか?テストする境界年齢は6つあります:
17、18、19、20、21、22

22
@FilipBartuzi、メソッドは男性が21歳以上かどうかをチェックしていると思いますか?現在書かれているように、それはそれをしません、彼らが22+である場合にのみ真実です。ただし、英語の「Over 21」は「21+」を意味します。そのため、コードにバグがあります。このようなバグは、境界値、つまりこの場合は男性の場合は20、21、22、女性の場合は17,18,19をテストすることによってキャプチャされます。したがって、少なくとも6つのテストが必要です。
デビッドアルノ

6
0と-1の場合は言うまでもありません。人が-1歳になるとはどういう意味ですか?APIが無意味な何かを返す場合、コードは何をすべきですか?
ラバーダック

9
パラメータとして人物オブジェクトを渡した場合、これはテストがはるかに簡単になります。
ジェフ

回答:


72

いいえ、統合テスト単体テストのカバレッジを複製するだけではいけません。彼らいくつかの報道を複製するかもしれませんが、それはポイントではありません。

単体テストのポイントは、特定の小さな機能が意図したとおりに正確かつ完全に機能することを確認することです。単体テストでam_i_old_enoughは、さまざまな年齢のデータ、確かにしきい値に近いデータ、おそらくはすべての発生年齢のデータをテストします。このテストを作成した後、の整合性がam_i_old_enough再び問題になることはありません。

統合テストのポイントは、システム全体、または相当数のコンポーネントの組み合わせが一緒に使用されたときに正しいことを行うことを確認することです。顧客は、あなたが書いた特定のユーティリティ機能については気にしません。そうしないと、規制当局が自分のロバを持つようになるため、未成年者からのアクセスに対してWebアプリが適切に保護されることを気にします。

ユーザの年齢を確認することである1つのその機能のごく一部が、統合テストは、あなたの効用関数が正しいしきい値を使用するかどうかをチェックしません。呼び出し元がそのしきい値に基づいて正しい決定を行うかどうか、ユーティリティ関数がまったく呼び出されるかどうか、アクセスの他の条件が満たされるかどうかなどをテストします。

両方のタイプのテストが必要な理由は、基本的に、実行に必要なコードベースを通るパスのシナリオの組み合わせが爆発的に増加するためです。ユーティリティ関数に約100の入力があり、何百ものユーティリティ関数がある場合、すべての場合に正しいことが起こることを確認するには、非常に多くのテストケースが必要になります。ユニットテスト実証されているように、非常に小さなスコープですべてのケースをチェックし、次にこれらのスコープの共通、関連、または可能性の高い組み合わせをチェックすることで、これらの小さなスコープが既に正しいと仮定して、システムが実行しているというかなり自信のある評価を得ることができますテストする代替シナリオにdrれずに、何をすべきか。


6
「テスト用の代替シナリオにdrれずに、システムが本来の動作をしているというかなり自信のある評価を得ることができます。」ありがとうございました。誰かが健全性を備えた自動化されたテストにアプローチするのが大好きです。
jpmc26

1
JB Rainsbergerは、テストと、「統合テストは詐欺」と呼ばれる最後の段落で書いているコンビナトリアル爆発について素晴らしい話をしています。統合テストについてはそれほどではありませんが、それでも非常に興味深いものです。
バートヴァンニーロップ

The customer doesn't care about a particular utility function you wrote, they care that their web app is properly secured against access by minors->それは非常に賢い考え方です、ありがとう!問題は、自分でプロジェクトを行うときです。それはプログラマであることと同じ瞬間のプロダクトマネージャーであることの間、あなたの考え方を分割するのは難しい
フィリップBartuzi

14

短い答えは「いいえ」です。より興味深い部分は、この状況が発生する理由/方法です。

厳密な慣行に従わないコードの厳密なテスト慣行(単体テストと統合テスト、モックなど)を順守しようとしているため、混乱が生じていると思います。

それは、コードが「間違っている」とか、特定のプラクティスが他のプラクティスより優れているということではありません。単純に、テストプラクティスによって行われた仮定の一部がこの状況に当てはまらない場合があり、コーディングプラクティスとテストプラクティスで同様のレベルの「厳格さ」を使用すると役立つ場合があります。または、少なくとも、それらが不均衡である可能性があることを認めるため、いくつかの側面が適用不能または冗長になる可能性があります。

最も明白な理由は、関数が2つの異なるタスクを実行していることです。

  • 見上げるPersonその名前に基づいて。これには、Personおそらく他の場所で作成/保存されたオブジェクトを検出できることを確認するために、統合テストが必要です。
  • Person性別に基づいて、a が十分に古いかどうかを計算します。これには、計算が期待どおりに実行されることを確認するための単体テストが必要です。

これらのタスクを1つのコードブロックにグループ化することにより、他のタスクなしでは実行できません。計算を単体テストする場合、Person(実際のデータベースまたはスタブ/モックから)ルックアップする必要があります。ルックアップがシステムの残りの部分と統合されていることをテストする場合、年齢の計算も実行する必要があります。その計算で何をすべきでしょうか?無視するか、確認する必要がありますか?それはあなたがあなたの質問で説明している正確な苦境のようです。

代替案を想像する場合、独自の計算を行うことができます。

def is_old_enough?(person)
   if person.male?
      return person.age > 21
   else 
      return person.age > 18
   end
end

これは純粋な計算なので、統合テストを実行する必要はありません。

ルックアップタスクを個別に記述したい場合もあります。

def person_from_name(name = 'filip')
   return Person::API.new(name)
end

ただし、この場合、機能は非常に近いためPerson::API.new、代わりにそれを使用する必要があります(デフォルト名が必要な場合は、クラス属性などの別の場所に保存する方が良いでしょうか?)

Person::API.new(またはperson_from_name)統合テストを作成する際に注意する必要があるのは、期待通りの結果が得られるかどうかだけですPerson。年齢ベースの計算はすべて他の場所で処理されるため、統合テストではそれらを無視できます。


11

Killianの答えに追加したいもう1つのポイントは、ユニットテストが非常に高速で実行されるため、1000のテストを実行できることです。統合テストは通常​​、Webサービス、データベース、またはその他の外部依存関係を呼び出しているため、時間がかかります。そのため、統合シナリオで同じテスト(1000秒)を実行することはできません。

また、通常、ユニットテストはビルド時に(ビルドマシンで)実行され、統合テストは環境/マシンに展開した後に実行れます。

通常、ビルドごとに数千のユニットテストを実行し、展開ごとに100以上の価値の高い統合テストを実行します。各ビルドを展開することはできませんが、統合テストを展開するために実行するビルドが実行されるため、問題ありません。通常、これらのテストを10〜15分以内に実行するように制限します。これは、展開を長くしすぎないようにするためです。

さらに、週次のスケジュールで、週末またはその他のダウンタイムでより多くのシナリオをカバーする統合テストの回帰スイートを実行する場合があります。より多くのシナリオがカバーされるため、これらは15分以上かかる場合がありますが、通常は誰も土/日で作業していないため、テストにもっと時間をかけることができます。


動的言語には適用されません(つまり、ビルドステージなし)
Filip Bartuzi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.