フェイク、モック、スタブの違いは何ですか?


705

私はこれらの用語の使い方を知っていますが、単体テストの偽装模倣、およびスタブの受け入れられた定義があるかどうか疑問に思っていますか?テスト用にこれらをどのように定義しますか?それぞれを使用する状況を説明してください。

ここに私がそれらを使う方法があります:

Fake:インターフェースを実装するが、固定データを含み、ロジックを含まないクラス。実装に応じて、単に「良い」または「悪い」データを返します。

Mock:インターフェイスを実装し、値を動的に設定して特定のメソッドから返す/例外をスローする機能を可能にし、特定のメソッドが呼び出されたかどうかを確認する機能を提供するクラス。

スタブ:メソッドが呼び出されたかどうかが確認されないことを除いて、モッククラスに似ています。

モックとスタブは手動で生成することも、モックフレームワークで生成することもできます。偽のクラスは手動で生成されます。私はモックを主に使用して、クラスと依存クラス間の相互作用を検証します。相互作用を確認し、コードを介して代替パスをテストした後、スタブを使用します。私は主にデータの依存関係を抽象化するため、またはモック/スタブが毎回設定するには面倒すぎるときに、偽のクラスを使用しています。


6
ええと、あなたは基本的にそれをすべてあなたの「質問」で言っていました:)それらはそれらの用語のかなりよく受け入れられた定義だと思います
Eran Galperin

2
WikipediaのFakeの定義はこれとは異なり、Fakeは「実際のデータベースアクセスを行う代わりに、テストでメモリ内データベースを使用するなど、より単純な実装として使用される」と断言します。en.wikipedia.org/ wiki / Test_doubleを
ズマライフガード2017

2
ロバートC.マーティン(ボブおじさん)によるすばらしい説明とともに、私は次のリソースから多くを学びました:The Clean Mocker on The Clean Code Blog。それは、ダミー、テストダブル、スタブ、スパイ、(真の)モックと偽物の違いと微妙さを説明しています。また、Martin Fowlerについても触れており、ソフトウェアテストの歴史についても少し説明しています。
エリック

testing.googleblog.com/2013/07/…(短い1ページの要約)。
ShreevatsaR 2019

これを説明する私の
見解は次のとおり

回答:


548

あなたはいくつかの情報を得ることができます:

以下からのモックとスタブに関するMartin Fowler氏

偽のオブジェクトには実際に機能する実装がありますが、通常はいくつかのショートカットを使用するため、プロダクションには適しません

スタブは、テスト中に行われた呼び出しに対する定型の回答を提供します。通常、テスト用にプログラムされたもの以外にはまったく応答しません。スタブは、「送信」されたメッセージを記憶する電子メールゲートウェイスタブや、「送信」されたメッセージの数だけを記録するなど、呼び出しに関する情報を記録する場合もあります。

モックとは、ここで話していることです。事前にプログラムされたオブジェクトで、期待される呼び出しの仕様を形成します。

xunitpatternから:

偽物:SUTが依存するコンポーネントによって提供されるのと同じ機能の非常に軽量な実装を取得または構築し、実際の代わりに使用するようにSUTに指示します。

スタブ:この実装は、SUT内のテストされていないコード(Xページの「プロダクションバグ」を参照)を実行する値(または例外)でSUTからの呼び出しに応答するように構成されています。テストスタブを使用するための重要な兆候は、SUTの間接入力を制御できないためにテストされていないコードがあることです。

SUT(テスト対象システム)が依存するオブジェクトと同じインターフェイスを実装するモックオブジェクト。SUTでメソッドを呼び出すことによる副作用を観察できないことに起因するテストされていない要件(Xページの「プロダクションバグ」を参照)を回避するために動作検証を行う必要がある場合は、モックオブジェクトを観察ポイントとして使用できます。

個人的に

私はモックとスタブを使用して単純化しようとします。テストしたクラスに設定された値を返すオブジェクトである場合は、Mockを使用します。テストするインターフェイスまたは抽象クラスを模倣するためにスタブを使用します。実際、何と呼んでもかまいません。これらはすべて、本番環境では使用されず、テスト用のユーティリティクラスとして使用されます。


9
Martin Fowlerの見積もりと比較して、xUnitPattern見積もりではスタブとフェイクの定義が逆になっているように思えます。また、マーティンファウラーのスタブとフェイクの定義は、tvanfossonの元の質問の定義と比較して逆転しています。実際には、これらの2つの用語の一般的に受け入れられている定義はありますか、それは単にあなたが話している相手に依存していますか?
Simon Tewsi 2013年

3
「モックとスタブを使用して簡素化を試みる」の+1。いい考えだ!
Brad Cupit 2013年

4
MockとStubだけを使用するのはどうでしょうか。すべてのテストdoubleには目的があり、したがって、その用途があります。
Hector Ordonez 2014年

1
MFの定義でFakeとMockの違いがわかりません。
IdontCareAboutReputationPoints 2015

2
@MusuNaji:MFの定義では、Fakeのインターフェースの実装があることを除いて、Fakeの会話に関して「期待」はありません。一方、モックはチャレンジされます(このメソッドは呼び出されましたか?)。
dbalakirev 2017年

205

スタブ -メソッド呼び出しに対する定義済みの回答を提供するオブジェクト。

モック -期待を設定するオブジェクト。

Fake-(テスト目的で)機能が制限されたオブジェクト(例:偽のWebサービス)。

Test Doubleは、スタブ、モック、フェイクの総称です。しかし、非公式に、人々は単に彼らを単にモックと呼ぶのを聞くでしょう。


4
誰かがこの文脈で「缶詰の答え」とは何かを私に説明して定義できますか?
MasterMastic 2013年

14
計算された値ではなく、明示的な値。
マイク

最終的に!私が理解できるいくつかの定義!これらの定義に基づいて、次に、googletest(GTEST)/ googlemock(gmock)が作成できるように、モックオブジェクトはまた、スタブであることができEXPECT_CALL()た力モック方法用いて特定の入力に基づいて特定の出力、のSを.WillOnce(Invoke(my_func_or_lambda_func))(または有する.WillRepeatedly())タイプに添付された構文EXPECT_CALL()。使用例のいくつかInvoke()は、ここでの私の長い答えの下部にある異なる状況で見ることができます:stackoverflow.com/a/60905880/4561887
ガブリエルステープルズ

Gmockに関するドキュメントInvoke()はこちらです:github.com/google/googletest/blob/master/googlemock/docs/…。とにかく、結論は次のとおりです。ほとんどのモックはスタブではありませんが、Googleモック(gmock)を使用すると、モックスタブの両方を簡単に作成できます
ガブリエルステープルズ

モックはスタブのスーパーセットであり、定義済みの回答を返すことができますが、開発者が期待を設定することもできます。IMOの特定のライブラリは、すべてのテストダミーの境界線を曖昧にします。
ルーク

94

この質問が長い間出回っていてRoy Osheroveの "The Art of Unit Testing"に基づいた答えを誰も提供していないことに驚いています。

「3.1スタブの紹介」では、スタブを次のように定義しています。

スタブは、システム内の既存の依存関係(またはコラボレーター)の制御可能な置き換えです。スタブを使用すると、依存関係を直接処理せずにコードをテストできます。

また、スタブとモックの違いを次のように定義します。

モックとスタブについて覚えておくべき主なことは、モックはスタブとまったく同じですが、モックオブジェクトに対してアサートするのに対して、スタブに対してアサートしないということです。

フェイクは、スタブとモックの両方に使用される名前です。たとえば、スタブとモックの違いを気にしない場合などです。

Osheroveがスタブとモックを区別する方法は、テストのために偽物として使用されるクラスは、スタブとモックの両方になる可能性があることを意味します。それが特定のテストに適しているかどうかは、テストでチェックをどのように記述するかによって完全に異なります。

  • テストでテスト中のクラスの値、または実際には偽物以外の値をチェックする場合、偽物がスタブとして使用されました。これは、テスト対象のクラスが使用する値を提供するだけで、そのクラスの呼び出しから返された値から直接、またはその呼び出しの結果として(ある状態で)副作用を引き起こすことによって間接的に提供されます。
  • テストが偽の値をチェックするとき、それはモックとして使用されました。

クラスFakeXがスタブとして使用されるテストの例:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, cut.SomeProperty);

fakeため、インスタンスがスタブとして使用されているAssert使用していないfakeすべてで。

テストクラスXがモックとして使用されるテストの例:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, fake.SomeProperty);

この場合、Assertはの値をチェックしfake、その偽物をモックにします。

もちろん、これらの例は非常に工夫されていますが、この違いには大きなメリットがあると思います。それはあなたがあなたがものをどのようにテストしているのか、そしてあなたのテストの依存関係がどこにあるのかをあなたに知らせます。

私はオセロベに同意します

純粋な保守性の観点から、モックを使用したテストでは、モックを使用しない場合よりも問題が発生します。それは私の経験でしたが、私はいつも何か新しいことを学んでいます。

偽物に対してアサートすることは、テストがまったくテスト対象ではないクラスの実装に大きく依存するため、本当に避けたいものです。つまりActualClassUnderTest、の実装がClassUsedAsMock変更されたため、クラスのテストが壊れ始める可能性があります。そして、それは私に悪臭を放ちます。のテストは、変更されたActualClassUnderTestときにのみ中断するのActualClassUnderTestが望ましいです。

特にあなたがTDDサブスクライバーのモック奏者タイプである場合、偽物に対してアサートを書くことは一般的な慣行であることを私は理解しています。私はクラシック主義のキャンプでマーティンファウラーとしっかりと一緒にいると思います(マーティンファウラーの「モックはスタブではない」を参照してください)。

ここで定義されているモックを避けなければならない理由についての楽しい読み物は、「ファウラーモック奏者の古典主義者」をググってください。あなたはたくさんの意見を見つけるでしょう。


30

トップ投票の回答で述べたように、Martin FowlerはMocks Are n't Stubsのこれらの違い、特に小見出しThe Difference Between Mocks and Stubsについて説明しているので、必ずその記事を読んでください。

むしろに焦点を当てたよりもどのようにこれらの事が異なっている、私はよりに集中する啓発と思う、なぜこれらが明確な概念です。それぞれが異なる目的のために存在します。

偽物

偽物はその振る舞う「自然」の実装ですが、「本物」ではありません。これらはあいまいな概念であるため、人によって物事が偽物になる理由に対する理解が異なります。

偽物の1つの例は、インメモリデータベースです(:memory:ストアでsqliteを使用するなど)。データが永続化されていないため、これを本番環境で使用することは決してありませんが、テスト環境で使用するデータベースとしては完全に適切です。また、「実際の」データベースよりもはるかに軽量です。

別の例として、おそらく本番環境で何らかのオブジェクトストア(Amazon S3など)を使用しますが、テストではオブジェクトをディスク上のファイルに保存するだけで済みます。次に、「ディスクに保存」の実装は偽物になります。(または、代わりにメモリ内ファイルシステムを使用して、「ディスクに保存」操作を偽ることもできます。)

3番目の例として、キャッシュAPIを提供するオブジェクトを想像してください。正しいインターフェイスを実装しているが、キャッシュをまったく実行せず、常にキャッシュミスを返すオブジェクトは、一種の偽物です。

偽の目的はしない被試験システムの挙動に影響を与えるために、むしろする実装簡素化(不要又はヘビー依存性を除去することにより)試験を。

スタブ

スタブは「不自然」に振る舞う実装です。これは、特定の出力で特定の入力に応答するように(通常はテスト設定によって)事前構成されています。

スタブの目的は、テスト中のシステムを特定の状態にすることです。たとえば、REST APIとやり取りするコードのテストを作成している場合、常に定型応答を返すか、特定のエラーでAPIリクエストに応答するAPIを使用てREST APIをスタブ化できます。このようにして、システムがこれらの状態にどのように反応するかについてアサーションを行うテストを作成できます。たとえば、APIが404エラーを返した場合にユーザーが受け取る応答をテストします。

スタブは通常、応答するように指示した正確な対話にのみ応答するように実装されます。しかし、何かをスタブにする主要な機能はその目的です。スタブはすべて、テストケースのセットアップに関するものです。

モック

モックはスタブに、しかしと類似している検証で追加。モックの目的は、テスト対象システムは、依存関係と相互作用する方法についての主張を作ることです

たとえば、ファイルをWebサイトにアップロードするシステムのテストを作成している場合、ファイルを受け入れ、アップロードされたファイルが正しいことを表明するために使用できるモックを構築できます。または、小規模では、オブジェクトのモックを使用して、テスト対象のシステムがモックされたオブジェクトの特定のメソッドを呼び出すことを確認するのが一般的です。

モックは、特定のテスト方法論である相互作用テストに関連付けられています。システムの相互作用よりもシステムの状態をテストすることを好む人は、モックを使用するのは控えめです。

ダブルスをテストする

偽物、スタブ、モックはすべてテストダブルスのカテゴリに属します。テストdoubleは、テスト何かの代わりに使用するオブジェクトまたはシステムです。ほとんどの自動化されたソフトウェアテストでは、何らかのテストダブルを使用します。他の種類のテストダブルには、ダミー値スパイ、I / O ブラックホールが含まれます。


11

スタブとモックの使用法を説明するために、Roy Osheroveの「 The Art of Unit Testing」に。

想像してみてください。LogAnalyzerアプリケーションがあり、ログを印刷するという唯一の機能があります。Webサービスと通信する必要があるだけでなく、Webサービスがエラーをスローした場合、LogAnalyzerはエラーを別の外部依存関係に記録し、電子メールでWebサービス管理者に送信する必要があります。

LogAnalyzer内でテストするロジックは次のとおりです。

if(fileName.Length<8)
{
 try
  {
    service.LogError("Filename too short:" + fileName);
  }
 catch (Exception e)
  {
    email.SendEmail("a","subject",e.Message);
  }
}

Webサービスが例外をスローしたときに、LogAnalyzerが電子メールサービスを正しく呼び出すことをどのようにテストしますか?私たちが直面している質問は次のとおりです。

  • Webサービスをどのように置き換えることができますか?

  • 電子メールサービスへの呼び出しをテストできるように、Webサービスからの例外をどのようにシミュレートできますか?

  • メールサービスが正しく呼び出されたか、またはまったく呼び出されたことをどのように知ることができますか?

Webサービスのスタブを使用することで、最初の2つの質問に対処できます。3番目の問題を解決するには、メールサービスにモックオブジェクトを使用できます

偽物とは、スタブまたはモックのいずれかを表すために使用できる一般的な用語です。このテストでは、2つの偽物を使用します。1つはメールサービスのモックです。これは、正しいパラメーターがメールサービスに送信されたことを確認するために使用します。もう1つは、Webサービスからスローされた例外をシミュレートするために使用するスタブです。テストが正しく実行されることを確認するためだけに、テスト結果を検証するためにWebサービスの偽物を使用しないため、これはスタブです。電子メールサービスは、正しく呼び出されたと断言するため、モックです。

[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
 public void Analyze_WebServiceThrows_SendsEmail()
 {
   StubService stubService = new StubService();
   stubService.ToThrow= new Exception("fake exception");
   MockEmailService mockEmail = new MockEmailService();

   LogAnalyzer2 log = new LogAnalyzer2();
   log.Service = stubService
   log.Email=mockEmail;
   string tooShortFileName="abc.ext";
   log.Analyze(tooShortFileName);

   Assert.AreEqual("a",mockEmail.To); //MOCKING USED
   Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
   Assert.AreEqual("subject",mockEmail.Subject);
 }
}

9

その上でアサートするものはモックオブジェクトと呼ばれ、テストの実行に役立つその他すべてはスタブです。


1
他の回答は非常に詳細で非常に優れています。これにより、違いが明確になり、違いが出やすくなります。賛成しないのは難しいことです。gj!
マリオガルシア

6

テストを表現力のあるものにすることの問題です。テストで2つのオブジェクト間の関係を記述したい場合は、モックに期待を設定します。テストで興味深い動作をさせるためにサポートオブジェクトを設定している場合、戻り値をスタブ化します。


6

Arrange-Act-Assertに精通している場合、スタブとモックの違いを説明する1つの方法は、スタブは入力状態を配置するためのものであり、モックは次のように配置セクションに属していることです。結果をアサートするためのアサートセクション。

ダミーは何もしません。これらはパラメーターリストを埋めるためのものであり、未定義のエラーやnullエラーが発生することはありません。また、厳密に型指定された言語で型チェッカーを満たすために存在するため、コンパイルして実行することができます。


3

スタブ、フェイク、モックは、ソースによって意味が異なります。チームの内部規約を紹介し、その意味に同意することをお勧めします。

私は2つのアプローチを区別することが重要だと思います:-動作検証(動作置換を意味します)-終了状態検証(動作エミュレーションを意味します)

エラーが発生した場合は、メール送信を検討してください。動作検証を行うとき-メソッドSendIEmailSender一度実行されたことを確認します。そして、あなたはこのメソッドの戻り結果をエミュレートする必要があり、送信されたメッセージのIdを返します。つまり、「私はそれSendが呼び出されることを期待しています。そして、どの呼び出しに対してもダミー(またはランダム)IDを返すだけです」と言います。これは動作検証です: emailSender.Expect(es=>es.Send(anyThing)).Return((subject,body) => "dummyId")

状態の検証を行うときはTestEmailSender、実装を作成する必要がありますIEmailSender。そして実装Send方法-いくつかのオブジェクトの配列のような将来の状態検証に使用されるいくつかのデータ構造への入力を保存することによってSentEmails、それはあなたがSentEmails期待される電子メールを含むことを確認することをテストします。これは状態の検証です: Assert.AreEqual(1, emailSender.SentEmails.Count)

私の読書から、行動の検証は通常Mocksと呼ばれることがわかりました。また、状態の検証は通常、スタブまたはフェイクと呼ばれます。


非常に詳細で鮮明な定義。
shyam sundar singh tomar

2

スタブフェイクは、入力パラメーターに基づいて応答を変えることができるという点でオブジェクトです。それらの主な違いは、フェイクはスタブよりも実際の実装に近いということです。スタブには、基本的に、予期される要求に対するハードコーディングされた応答が含まれています。例を見てみましょう:

public class MyUnitTest {

 @Test
 public void testConcatenate() {
  StubDependency stubDependency = new StubDependency();
  int result = stubDependency.toNumber("one", "two");
  assertEquals("onetwo", result);
 }
}

public class StubDependency() {
 public int toNumber(string param) {
  if (param == “one”) {
   return 1;
  }
  if (param == “two”) {
   return 2;
  }
 }
}

モックは偽物とスタブからのステップアップです。モックはスタブと同じ機能を提供しますが、より複雑です。APIのメソッドを呼び出す必要がある順序を指示するルールを定義できます。ほとんどのモックは、メソッドが呼び出された回数を追跡でき、その情報に基づいて反応できます。モックは通常、各呼び出しのコンテキストを知っており、状況によって反応が異なります。このため、モックはモックしているクラスの知識を必要とします。スタブは通常、メソッドが呼び出された回数や、メソッドのシーケンスが呼び出された順序を追跡できません。モックは次のようになります。

public class MockADependency {

 private int ShouldCallTwice;
 private boolean ShouldCallAtEnd;
 private boolean ShouldCallFirst;

 public int StringToInteger(String s) {
  if (s == "abc") {
   return 1;
  }
  if (s == "xyz") {
   return 2;
  }
  return 0;
 }

 public void ShouldCallFirst() {
  if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
   throw new AssertionException("ShouldCallFirst not first thod called");
  ShouldCallFirst = true;
 }

 public int ShouldCallTwice(string s) {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
  if (ShouldCallAtEnd)
   throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
  if (ShouldCallTwice >= 2)
   throw new AssertionException("ShouldCallTwice called more than twice");
  ShouldCallTwice++;
  return StringToInteger(s);
 }

 public void ShouldCallAtEnd() {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
  if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
  ShouldCallAtEnd = true;
 }

}

1

fake objectインターフェイスの実際の実装(プロトコル)であるか、または相続または作成するために使用できる他の方法を使用して延び- である依存性。通常、依存関係を置き換える最も簡単なソリューションとして開発者が作成します

stub object戻り値を定義するための追加および事前定義済み(開発者による)状態を備えた裸のオブジェクト(0、nil、およびロジックなしのメソッド)です。通常はフレームワークによって作成されます

mock objectは非常に似てstub objectいますが、何かが起こったかどうかを確認するために、プログラムの実行中に追加の状態が変更されます(メソッドが呼び出されました)。

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