6

Mockito を使用して「リーダー」型クラスをモックしようとしています。データストリームリーダーを考えてみてください。さまざまなデータ型を読み取り、読み取りごとに内部ポインターを進めるメソッドがあります。

public interface Reader {
    int readInt();
    short readShort();
}

テスト対象のクラスは、データ ストリームからさまざまなデータ構造を読み取ります。例えば、

public class Somethings {
    public List<Something> somethings;

    public Somethings(Reader reader) {
        somethings = new List<Something>();
        int count = reader.readInt();
        for (int i=0; i<count; i++) {
            somethings.add(readSomething(reader));
        }
    }

    private Something readSomething(Reader reader) {
        int a = reader.readInt();
        short b = reader.readShort();
        int c = reader.readInt();
        return new Something(a, b, c);
    }
}

最後に、テストがあります。

public class SomethingsTest {
    @Test
    public void testSomethings() {
        Reader reader = Mockito.mock(Reader.class);

        readCount(reader, 2);
        readSomething(reader, 1, 2, 3);
        readSomething(reader, 4, 5, 6);

        Somethings somethings = new Somethings(reader);
        Assert.assertEqual(2, somethings.size());
        Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0));
        Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1));
    }

    private void readCount(Reader reader, int count) {
        when(reader.readInt()).thenReturn(count);
    }

    private void readSomething(Reader reader, int a, short b, int c) {
        when(reader.readInt()).thenReturn(a);
        when(reader.readShort()).thenReturn(b);
        when(reader.readInt()).thenReturn(c);
    }
}

残念ながら、これは機能しません。reader.readInt() は、呼び出しごとに常に 6 を返します。そして、6 が返される理由を理解しています。それは私の質問ではありません。

テストを修正するために考えられる 2 つのオプションがありますが、どちらも特に好きではありません。

最初のオプションは次のようになります。

public class SomethingsTest {
    @Test
    public void testSomethings() {
        Reader reader = Mockito.mock(Reader.class);

        when(reader.readInt())
            .thenReturn(2)
            .thenReturn(1)
            .thenReturn(3)
            .thenReturn(4)
            .thenReturn(6);
        when(reader.readShort())
            .thenReturn(2)
            .thenReturn(5);

        Somethings somethings = new Somethings(reader);
        Assert.assertEqual(2, somethings.size());
        Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0));
        Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1));
    }
}

これは機能するはずですが、非常にモノリシックで面倒です。どのリターンがどのストラクチャーのどの部分に対するものかを確認するのは困難です。なぜなら、それらはすべて混ざり合っており、ストラクチャーがないからです。

私が考えることができる2番目のオプションは次のようなものです:

public class SomethingsTest {
    @Test
    public void testSomethings() {
        Reader reader = Mockito.mock(Reader.class);

        NewOngoingStubbing readIntStub = when(reader.readInt());
        NewOngoingStubbing readShortStub = when(reader.readShort());

        readCount(readIntStub, 2);
        readSomething(readIntStub, readShortStub, 1, 2, 3);
        readSomething(readIntStub, readShortStub, 4, 5, 6);

        Somethings somethings = new Somethings(reader);
        Assert.assertEqual(2, somethings.size());
        Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0));
        Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1));
    }

    private void readCount(NewOngoingStubbing readIntStub, int count) {
        readIntStub.thenReturn(count);
    }

    private void readSomething(NewOngoingStubbing readIntStub,
            NewOngoingStubbing  readShortStub, int a, short b, int c) {
        readIntStub.thenReturn(a);
        readShortStub.thenReturn(b);
        readIntStub.thenReturn(c);
    }
}

これにより、少なくとも元の構造が維持されますが、スタブ化されたオブジェクトに対して行うメソッド呼び出しごとに個別のオブジェクトを渡す必要があるのは... うーん。

このテストを実行する最もクリーンな方法は何ですか? ここに欠けているオプションはありますか?活用できる機能はありますか? 今夜Mockitoを使い始めたばかりなので、何かが足りない可能性があります。

4

3 に答える 3

7

Mockito は継続的なスタブ処理をネイティブで行います。最初の例は問題ありませんが、これも機能するはずです:

when(reader.readInt()).thenReturn(2, 1, 3, 4, 6);

そのドキュメントはhereです。

特に複雑なやり取りを処理するものがある場合は、独自のスタブ クラスをロールアウトしてもかまいません。現実的なデータでいくつかの偽物を初期化し、それを使用すると、クラスがどのように連携するかについて、Mockito よりも明確な例が得られることに気付くかもしれません。その場合は、慣習よりも明快に行ってください。Mockito は IMO で最高のモッキング フレームワークですが、私はまだ独自のフレームワークを作成することがあります。

于 2012-10-27T10:37:15.703 に答える
2

それを処理するクラスを作成できます。

private static class ReaderStubber {
    private final Reader reader = Mockito.mock(Reader.class);
    private final NewOngoingStubbing readIntStub = when(reader.readInt());;
    private final NewOngoingStubbing readShortStub = when(reader.readShort());;

    public Reader getReader() {
        return reader;
    }    

    private void readCount(int count) {
        readIntStub.thenReturn(count);
    }

    private void readSomething(int a, short b, int c) {
        readIntStub.thenReturn(a);
        readShortStub.thenReturn(b);
        readIntStub.thenReturn(c);
    }   
}

しかし、問題は、これを Mockito で本当に行う必要があるかということです。すべてを嘲笑すべきではありません。おそらく、内部Readerにいくつかのテスト用のスタブを実装するだけの方が良いでしょう。List<Integer>


(編集) また、これが可能であれば、 を再設計しReaderて不変にし、いくつかの を返す必要があるかもしれませんNewOngoingReading。多くの場合 (常にではありませんが)、テストが難しいものは再設計する方が適切です。また、同期を処理する必要はありません。

于 2012-10-27T10:43:37.090 に答える
2

Mockito の標準メソッドが動作をシミュレートするメカニズムを提供しない場合は、独自の を実装することに頼ることができますAnswer。これは手間がかかりますが、柔軟性が高まります。

正確な要件に応じて、たとえば、リクエスト タイプ (または)Answerに関係なく、数値のリストから新しい要素を返す を作成できます。変数は、結果を設定するために使用するすべての関数からアクセスできるメンバーにすることができます。intshortreadList

final List<Integer> readList = new ArrayList<>();
// ... Fill readList with answers

Answer answerFromList = new Answer() {
    Object answer(InvocationOnMock invocation) {
        // Remove and return first element
        return readList.remove(0);
    }
}

when(reader.readInt()).thenAnswer(answerFromList);
when(reader.readShort()).thenAnswer(answerFromList);

これAnswerは提供されている によく似ているReturnElementsOfため、直接使用することもできます。

于 2015-02-16T10:43:14.190 に答える