Martin FowlerのMocks Aren's Stubsを含む、テストにおけるモッキングとスタブについてのさまざまな記事を読みましたが、その違いはまだわかりません。
Martin FowlerのMocks Aren's Stubsを含む、テストにおけるモッキングとスタブについてのさまざまな記事を読みましたが、その違いはまだわかりません。
回答:
スタブ
最大の違いは、あなたがすでに所定の動作で書いたスタブであることだと思います。したがって、テスト目的で偽装している依存関係を実装するクラス(抽象クラスまたはインターフェイスの可能性が最も高い)があり、メソッドはset応答でスタブされます。彼らは特別なことは何もせず、あなたはすでにあなたのテストの外でそれのためにスタブされたコードを書いているでしょう。
モック
モックは、テストの一環として、期待に合わせてセットアップする必要があるものです。モックはあらかじめ決められた方法で設定されていないため、テストでそれを実行するコードがあります。期待を設定するコードは何かを実行する前に実行する必要があるため、モックは実行時に決定されます。
モックとスタブの違い
モックで書かれたテストは通常、initialize -> set expectations -> exercise -> verify
テストのパターンに従います。事前に作成されたスタブは、initialize -> exercise -> verify
。
モックとスタブの類似点
両方の目的は、クラスまたは関数のすべての依存関係のテストを排除して、テストが証明しようとしていることにより焦点を絞って簡単にすることです。
オブジェクトの定義はいくつかありますが、実際にはありません。一般的な用語はtest doubleです。この用語には、ダミー、フェイク、スタブ、モックが含まれます。ます。
マーティン・ファウラーの記事によると:
- ダミーオブジェクトは渡されますが、実際には使用されません。通常、これらはパラメータリストの入力にのみ使用されます。
- 偽のオブジェクトは実際には機能する実装を持っていますが、通常はそれらをプロダクションに適さないようにするいくつかのショートカットを取ります(メモリ内データベースが良い例です)。
- スタブは、テスト中に行われた呼び出しに対する定型の回答を提供します。通常、テスト用にプログラムされたもの以外にはまったく応答しません。スタブは、「送信」されたメッセージを記憶する電子メールゲートウェイスタブや、「送信」されたメッセージの数だけを記録するなど、呼び出しに関する情報を記録する場合もあります。
- モックとは、ここで話していることです。事前にプログラムされたオブジェクトで、期待される呼び出しの仕様を形成します。
モックvsスタブ=行動テストvs状態テスト
テストの原則によると、テストごとに1つだけです。1つのテストに複数のスタブがある場合がありますが、一般的にはモックは1つだけです。
スタブを使用してライフサイクルをテストします。
モックでライフサイクルをテストする:
モックとスタブの両方のテストで、質問に対する答えが得られます。結果はどうなりますか?
モックを使用したテストは、次の点にも関心があります。
スタブは単純な偽のオブジェクトです。テストがスムーズに実行されるようにするだけです。
モックはよりスマートなスタブです。テストがパスすることを確認します。
以下に、それぞれの説明と、それに続く実際のサンプルを示します。
ダミー -を満たすための単なる偽の値API
。
例:テストに影響を与えないコンストラクターで多くの必須パラメーターを必要とするクラスのメソッドをテストする場合は、クラスの新しいインスタンスを作成するためにダミーオブジェクトを作成できます。
偽物 -一部の外部インフラストラクチャに依存している可能性があるクラスのテスト実装を作成します。(単体テストが実際に外部インフラストラクチャと相互作用しないことは良い習慣です。)
例:データベースにアクセスするための偽の実装を作成し、
in-memory
コレクションで置き換えます。
スタブ -メソッドをオーバーライドして、ハードコードされた値を返しますstate-based
。
例:テストクラスは、
Calculate()
完了するまでに5分かかるメソッドに依存します。5分待つのではなく、実際の実装をハードコードされた値を返すスタブに置き換えることができます。ほんの少しの時間しかかかりません。
モック - 状態ベースではなく非常に似てStub
いinteraction-based
ます。これはMock
、が何らかの値を返すことを期待するのではなく、メソッド呼び出しの特定の順序が行われたと想定することを意味します。
例:ユーザー登録クラスをテストしています。呼び出した後
Save
、それは呼び出す必要がありますSendConfirmationEmail
。
Stubs
とMocks
は実際にはのサブタイプでありMock
、どちらも実際の実装とテスト実装を入れ替えますが、特定の理由により異なります。
ではcodeschool.comのコース、ゾンビのためのRailsのテスト、彼らは用語のこの定義を与えます:
スタブ
メソッドを指定された結果を返すコードに置き換えるため。
モック
メソッドが呼び出されることを表明するスタブ。
したがって、Sean Copenhaverが彼の回答で述べたように、違いはモックが期待を設定する(つまり、アサーションを作成するか、呼び出されるかどうか、またはどのように呼び出されるか)ことです。
この質問についての最も単純で明確な答えは、Roy Osheroveの著書「The Art of Unit Testing(85ページ)」で与えられていると思います。
スタブを扱っていることを伝える最も簡単な方法は、スタブがテストに失敗することは決してないことに気づくことです。テストが使用するアサートは、常にテスト中のクラスに反しています。
一方、テストはモックオブジェクトを使用して、テストが失敗したかどうかを確認します。[...]
ここでも、モックオブジェクトは、テストが失敗したかどうかを確認するために使用するオブジェクトです。
つまり、フェイクに対してアサーションを作成している場合は、フェイクをモックとして使用していることを意味します。フェイクを使用してテストを実行せずにテストを実行している場合は、フェイクをスタブとして使用しています。
上記のすべての説明を読んで、要約してみましょう。
Mockは動作をテストするだけで、特定のメソッドが呼び出されることを確認しています。スタブは、特定のオブジェクトのテスト可能なバージョン(それ自体)です。
アップルのやり方とはどういう意味ですか?
メンタルモデルの使用で、説明や記事のすべてではなく、これを理解するのに本当に役立ちました。
あなたの子供がテーブルの上にガラス板を持っていて、彼がそれで遊んでいると想像してください。さて、あなたはそれが壊れることを恐れています。代わりに、あなたは彼にプラスチックプレートを与えます。それはモックになります(同じ動作、同じインターフェース、「よりソフトな」実装)。
さて、あなたはプラスチックの代替品を持っていないと言うので、「それをいじり続けると壊れる!」と説明します。これはスタブであり、事前に事前定義された状態を提供しました。
A ダミーは、彼も使用していなかったフォークだろう...とスパイはすでに働いていたことを使用したのと同じ説明を提供するようなものである可能性があります。
彼らの最も重要な違いは彼らの意図だと思います。
WHYスタブとWHYモックで説明してみましょう
Mac Twitterクライアントのパブリックタイムラインコントローラーのテストコードを書いているとしましょう
ここにテストサンプルコードがあります
twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
モックを書くことで、期待が満たされていることを確認することでオブジェクトのコラボレーション関係を発見しますが、スタブはオブジェクトの動作をシミュレートするだけです。
モックについてもっと知りたい場合は、この記事を読むことをお勧めします:http : //jmock.org/oopsla2004.pdf
非常に明確で実用的なこと:
スタブ:偽造されるクラス/オブジェクトのメソッドを実装し、常に必要なものを返すクラスまたはオブジェクト。
JavaScriptの例:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
モック:スタブと同じですが、メソッドが呼び出されたときに「検証」するロジックが追加されるため、実装がそのメソッドを呼び出していることを確認できます。
@mLevanが言うように、例としてユーザー登録クラスをテストしていると想像してください。Saveを呼び出した後、SendConfirmationEmailを呼び出す必要があります。
非常に愚かなコードの例:
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
ダブルテストを見てみましょう:
スタブ:スタブは、事前定義されたデータを保持し、テスト中に呼び出しに応答するためにそれを使用するオブジェクトです。など:メソッド呼び出しに応答するためにデータベースからデータを取得する必要があるオブジェクト。
モック:モックは、受け取った呼び出しを登録するオブジェクトです。テストアサーションでは、予想されるすべてのアクションが実行されたことをモックで確認できます。など:メール送信サービスを呼び出す機能。詳細については、これを確認してください。
フェイク本物の物体のように、彼らは両方見ているので、スタブやモックオブジェクト(手書きまたはその他)のいずれかを記述するために使用することができる一般的な用語です。
フェイクがスタブであるかモックであるかは、現在のテストでの使用方法によって異なります。インタラクションのチェック(アサート)に使用される場合、それはモックオブジェクトです。それ以外の場合は、スタブです。
偽物はテストがスムーズに実行されることを確認します。これは、将来のテストの読者が、ソースコードを読み取る必要なく(外部リソースに依存する必要なく)、偽のオブジェクトの動作を理解することを意味します。
テストランとはどういう意味ですか?
以下のコードの例:
public void Analyze(string filename)
{
if(filename.Length<8)
{
try
{
errorService.LogError("long file entered named:" + filename);
}
catch (Exception e)
{
mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
}
}
}
あなたは、テストしたいmailService.SendEMail()ちょうどその結果をシミュレートするために、偽のスタブerrorServiceクラスを作成する必要があるので、その後、テストコードをテストすることができるようになります、あなたは試験方法で例外をシミュレートする必要があることを行うには、メソッドをmailService.SendEMail()メソッド。ご覧のとおり、別のExternal Dependency ErrorServiceクラスからの結果をシミュレートする必要があります。
jMockの開発者による紙ではなく、オブジェクトではなくMock Rolesから:
スタブは、あらかじめ用意された結果を返すプロダクションコードのダミー実装です。モックオブジェクトはスタブとして機能しますが、ターゲットオブジェクトとその隣接オブジェクトとの相互作用を計測するアサーションも含まれます。
したがって、主な違いは次のとおりです。
要約すると、Fowlerの記事のタイトルから混乱を分散させようとしていますが、モックはスタブですが、スタブだけではありません。
私はThe Art of Unit Testingを読んでいて、次の定義に出くわしました。
偽物は本物の物体のように、彼らは両方見ているので、スタブやモックオブジェクト(手書きまたはその他)のいずれかを記述するために使用することができる一般的な用語です。フェイクがスタブであるかモックであるかは、現在のテストでの使用方法によって異なります。インタラクションのチェック(アサート)に使用される場合は、モックオブジェクトです。それ以外の場合は、スタブです。
UncleBob The Little Mockerによるこの興味深い記事に出会いました。すべての用語を非常に理解しやすい方法で説明しているため、初心者には役立ちます。マーティン・ファウラーズの記事は、特に私のような初心者には読みにくいです。
スタブはテストの実行に役立ちます。どうやって?テストの実行に役立つ値を提供します。これらの値自体は実際のものではなく、テストを実行するためだけに作成したものです。たとえば、HashMapを作成して、データベーステーブルの値と同様の値を取得します。したがって、データベースと直接対話する代わりに、ハッシュマップと対話します。
モックはテストを実行する偽のオブジェクトです。アサートする場所。
C#とMoqフレームワークを使用したモックとスタブの例を以下に示します。Moqにはスタブ用の特別なキーワードはありませんが、Mockオブジェクトを使用してスタブを作成することもできます。
namespace UnitTestProject2
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestClass]
public class UnitTest1
{
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
}
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(0);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
}
/// <summary>
/// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
{
// Arrange
var stubEntityRepository = new Mock<IEntityRepository>();
stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
.Returns("Stub");
const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
var entity = new EntityClass(stubEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
}
}
public class EntityClass
{
private IEntityRepository _entityRepository;
public EntityClass(IEntityRepository entityRepository)
{
this._entityRepository = entityRepository;
}
public string Name { get; set; }
public string GetNameWithPrefix(int id)
{
string name = string.Empty;
if (id > 0)
{
name = this._entityRepository.GetName(id);
}
return "Mr. " + name;
}
}
public interface IEntityRepository
{
string GetName(int id);
}
public class EntityRepository:IEntityRepository
{
public string GetName(int id)
{
// Code to connect to DB and get name based on Id
return "NameFromDb";
}
}
}
私は違いを説明するために私の答えでpythonの例を使用しました。
スタブ -スタブは、開発ライフサイクルの初期にクラスのメソッドを実装するために使用されるソフトウェア開発手法です。それらは一般に、既知のインターフェースの実装のプレースホルダーとして使用されます。インターフェースは確定または既知ですが、実装はまだ不明または確定されていません。スタブから始めます。これは、関数の定義を書き留め、後で使用するために実際のコードを残すことを意味します。利点は、メソッドを忘れることがなく、コードで確認しながらデザインについて引き続き検討できることです。スタブに静的な応答を返させて、コードの他の部分ですぐに応答を使用できるようにすることもできます。スタブオブジェクトは有効な応答を提供しますが、どの入力を渡しても静的であり、常に同じ応答を取得します。
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
モックオブジェクトは、モックテストケースで使用され、それらのオブジェクトで特定のメソッドが呼び出されることを検証します。モックオブジェクトは、制御された方法で実際のオブジェクトの動作を模倣するシミュレーションオブジェクトです。通常、モックオブジェクトを作成して、他のオブジェクトの動作をテストします。モックを使用すると、ユニットテストでは使用できないか扱いにくいリソースをシミュレートできます。
mymodule.py:
import os
import os.path
def rm(filename):
if os.path.isfile(filename):
os.remove(filename)
test.py:
from mymodule import rm
import mock
import unittest
class RmTestCase(unittest.TestCase):
@mock.patch('mymodule.os')
def test_rm(self, mock_os):
rm("any path")
# test that rm called os.remove with the right parameters
mock_os.remove.assert_called_with("any path")
if __name__ == '__main__':
unittest.main()
これは、rmを実行し、それが呼び出されたパラメーターをアサートする非常に基本的な例です。ここに示すように、関数だけでなくオブジェクトでもモックを使用できます。また、モックオブジェクトを使用してテスト用のスタブを置き換えることができるように、値を返すこともできます。
unittest.mockの詳細、python 2.xのメモモックはunittestに含まれていませんが、pip(pip install mock)を介してダウンロードできるダウンロード可能なモジュールです。
Roy Osheroveの「The Art of Unit Testing」も読んだことがありますが、同様の本がPythonとPythonの例を使用して書かれていれば素晴らしいと思います。誰かがそのような本を知っているなら、共有してください。乾杯:)
スタブは、テスト目的で作成された偽のオブジェクトです。モックは、予想される呼び出しが効果的に行われたかどうかを記録するスタブです。
スタブは空の関数で、テスト中に未処理の例外を回避するために使用されます。
function foo(){}
モックは、テスト中にOS、環境、またはハードウェアの依存関係を回避するために使用される人工的な機能です。
function foo(bar){ window = this; return window.toString(bar); }
アサーションと状態に関して:
参考文献
有効な答えはたくさんありますが、このフォームの叔父ボブについて言及する価値があると思います:https : //8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
例でこれまでで最高の説明!
モックは技術的で機能的なオブジェクトです。
モックはテクニカルです。バイトコード生成のおかげで、これは実際にモックライブラリ(EasyMock、JMockit、および最近ではMockitoがこれらで知られています)によって作成されています。
モック実装は、メソッドが呼び出されたときに特定の値を返すようにインストゥルメントできる方法で生成されますが、モックメソッドがいくつかの特定のパラメーター(厳密なチェック)またはパラメーター(厳密なチェックはありません)。
モックのインスタンス化:
@Mock Foo fooMock
行動の記録:
when(fooMock.hello()).thenReturn("hello you!");
呼び出しの確認:
verify(fooMock).hello()
これらは明らかに、Fooクラス/動作をインスタンス化/オーバーライドする自然な方法ではありません。そのため、技術的な側面について言及します。
ただし、モックは SUTから分離する必要があるクラスのインスタンスであるため、モックも機能します。そして、その動作を記録しておけば、スタブの場合と同じようにSUTで使用できます。
スタブは単なる機能オブジェクトです。これは、SUTから分離する必要があるクラスのインスタンスであり、それだけです。つまり、ユニットテスト中に必要なスタブクラスとすべての動作フィクスチャの両方を明示的に定義する必要があります。
たとえば、スタブhello()
を作成するには、Foo
クラスをサブクラス化する(またはクラスのインターフェースを実装する)必要があり、オーバーライドする必要がありますhello()
。
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
別のテストシナリオで別の値を返す必要がある場合は、おそらくreturnを設定する一般的な方法を定義する必要があります。
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
その他のシナリオ:副作用メソッド(戻り値なし)があり、そのメソッドが呼び出されたことを確認する場合、スタブクラスにブール値またはカウンターを追加して、メソッドが呼び出された回数をカウントする必要があります。
結論
多くの場合、スタブは単体テスト用に書き込むために多くのオーバーヘッド/コードを必要とします。箱から出してすぐに録音/検証機能を提供することのおかげで、モックが妨げるもの。
そのため、今日では、優れたモックライブラリの登場により、実際にはスタブアプローチが使用されることはほとんどありません。
Martin Fowlerの記事について:モックを使用している間は「モック奏者」のプログラマーだとは思わず、スタブを避けます。
しかし、本当に必要な場合(依存性がわずらわしい場合)にモックを使用し、モックがオーバーヘッドになる依存性を持つクラスをテストする場合は、テストスライスとミニ統合テストを優先します。