Test Double(Test Double)Mock VS Stubの理解

4849 ワード

本文の内容は主に以下の通りである.https://adamcod.es/2014/05/15/test-doubles-mock-vs-stub.html
Test Double自体が問題で、ネット上ではすでに「テスト身代わり」を使っている人がいて、自分で発明していません.なぜMockやStubではなくテスト身代わりと呼ばれているのですか?著者は混用現象が非常に深刻であることを発見したからだ.この文章はテストの身代わりについて詳しく説明し、分類した.
Dummies
Dummyは傀儡の意味です.Dummyはインタフェースの実装にすぎず、他のことは何もしていません.テストでは使用されず、コードの動作にも影響しません.例を挙げます.
private class FooDummy implements Foo
{
    public String bar() { return null; }
}

public class FooCollectionTest
{
    @Test
    public void it_should_maintain_a_count()
    {
        FooCollection sut = new FooCollection();
        sut.add(new FooDummy);
        sut.add(new FooDummy);
        assertEquals(2, sut.count());
    }
}

FooCollectionTestはFoo容器をテストするために使用されます.Fooオブジェクト自体のメソッドbarには関心がないので,FooDummyクラスを構築した.簡単に言えば、Dummyはオブジェクトの使用時のコンパイルの問題を解決するために使用されます.
Stubs
StubはDummyの強化版と考えられる.Dummyに実際のメソッドがないように、Stubは一般的に予め設定されたデータを返します.Stubを使用すると、テストで役立つチェックができます.例を挙げます.
private class FooStub implements Foo
{
    public String bar()
    {
        return "baz";
    }
}

public class FooCollectionTest
{
    @Test
    public void it_should_return_joined_bars()
    {
        FooCollection sut = new FooCollection();
        sut.add(new FooStub);
        sut.add(new FooStub);
        assertEquals("bazbaz", sut.joined());
    }
}

Fooコンテナのjoined関数は、コンテナ内のFooオブジェクトを接続するために使用されます.テストの目的を達成するためにbarは直接空に戻ったり、同じ文字(例えば「aa」)はテストの目的を達成できません.そこでbarをStubとして「baz」に戻し、接続した結果に意味があります.
Spies
Stubが内部状態を維持し、断言検査に使用される場合.Spy(詐欺師)にアップグレードされました例を挙げます.
private class ThirdPartyApiSpy implements ThirdPartyApi
{
    public int callCount = 0;

    public boolean hasMore(Response previousResponse)
    {
        if (this.callCount == 0) {
            return true;
        }
        return false;
    }

    public Response get(int page)
    {
        this.callCount++;
        return new DummyResponse;
    }
}

public class ApiConsumerTest
{
    @Test
    public void it_should_get_all_pages()
    {
        ThirdPartyApiSpy spy = new ThirdPartyApiSpy
        ApiConsumer sut = new ApiConsumer(spy);
        sut.fetchAll()
        assertEquals(2, spy.callCount);
    }
}

Fakes
FakeはStubのアップグレード版だと理解できます.戻り値を与えるだけでなく、実際のオブジェクトのようにテストオブジェクトとインタラクティブになります.たとえば、ストレージ・データを永続化するために、ファイルまたはデータベースにデータを書き込みます.しかし、ユニットテストではそうはいかないに違いありません.メモリオブジェクトを構築し、書き込みやデータベースをシミュレートすることができます.
private class InMemoryUserRepository implements UserRepository
{
    private UserCollection users = new UserCollection;

    public User load(UserIdentifier identifier)
    {
        if (!this.users.exists(identifier)) {
            throw new InvalidUserException;
        }

        return this.users.get(identifier);
    }

    public User find(UserIdentifier identifier)
    {
        if (!this.users.exists(identifier)) {
            return null;
        }

        return this.users.get(identifier);
    }

    public UserCollection fetchAll()
    {
        return this.users;
    }

    public boolean add(User user)
    {
        return this.users.add(user);
    }

    public boolean delete(User user)
    {
        return this.delete(user.getIdentifier());
    }

    public boolean delete(UserIdentifier identifier)
    {
        return this.users.remove(identifier);
    }
}

public class CreateUserServiceTest
{
    @Test
    public void it_should_save_a_new_user()
    {
        UserRepository userRepository = new InMemoryUserRepository;
        CreateUserService sut = new CreateUserService(userRepository);
        sut.createUser(new UserRequestStub);
        assertEquals(new UserCollectionStub, userRepository.fetchAll());
    }
}

ここではUserRequestStubとUserCollectionsStubを省略し,それらを実現するのも比較的簡単である.InMemoryUserRepositoryは、実際のUserRepositoryオブジェクトのようです.削除ユーザーを追加したり、ユーザーを検索したり、ユーザーをロードしたりすることができます.唯一の問題は、本当に永続的な話をすることができず、すべての製品コードとして使用できず、テストコードしか使用できません.
Mocks
前述したいくつかのテストの身代わりは比較的近い.少なくともテスト能力は多かれ少なかれありますが、Mockは全く違います.前のいくつかのテストエージェントは状態(state)に基づいて断言されたが,Mockは挙動(Behavior)に基づいて断言された.Mockは「foo()を呼び出してパラメータbarを携帯することを期待しています.期待に達しなければ、エラーを報告します!」と言います.
Mockを使用するには、一般的に3つのステップに分けられます.
  • 作成対象例
  • 定義動作
  • 断言呼び出しは所望の
  • に適合する.
    例を挙げます.
    public class FooCollectionTest
    {
        @Test
        public void it_should_return_joined_bars()
        {
            Foo fooMock = mock(Foo.class); // instance
            when(fooMock.bar()).thenReturn("baz", "qux"); // behaviour
    
            FooCollection sut = new FooCollection();
            sut.add(fooMock);
            sut.add(fooMock);
    
            assertEquals("bazqux", sut.joined());
            verify(fooMock, times(2)).bar(); // verify
        }
    }

    もし私たちがverify()を置いて見なければ、このテストの身代わりは依然としてStubです.ここで重要な違いはverify()です.Stubは断言状態にすぎず,Mock断言は正しいメソッドが正しい回数(メソッド呼び出しの順序やメソッド呼び出しで使用されるパラメータも含む)を呼び出される.