2011-12-04 10 views
5

文字が正しく文字化けしていないことを確認するために、文字の特定の順序を確認する必要があります。私はInOrderを使ってそれを書こうとしましたが、動作しないか、少なくともMockito 1.8.5では動作していないようです。同じ引数を持つコールの特定の順序についてMockitoでテストする方法は?

@Test 
public void inOrderTest() throws IOException{ 
    final String message = "Hello World!\n"; 

    for(char c : message.toCharArray()) 
     mockWriter.write(c); 

    final InOrder inOrder = inOrder(mockWriter); 
    for(char c : message.toCharArray()) 
     inOrder.verify(mockWriter).write(c); 
    inOrder.verifyNoMoreInteractions(); 
} 

テストは、上記のメッセージで失敗します。

Verification in order failure: 
mockWriter.write(108); 
Wanted 1 time: 
-> at  org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:62) 
But was 3 times. Undesired invocation: 
-> at org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:58) 

はどのようにしているためMockitoのテストを書くのですか?


EDIT:あなたは「L」に着くとMockitoを伝えるときためにhttp://code.google.com/p/mockito/issues/detail?id=296

答えて

19

私は以前の回答者に謝罪しました。私の意見では、答えを使うことは、Mockitoの基本的なアイデアの1つに直面しています。つまり、スタブと検証は2つの完全に別々のプロセスです。 Mockitoにはスタブ機能と検証機能があり、Mockitoのメーカーはこの2つを別々に保つために努力しました。アンサーはスタブを目的としています。回答が最も良い方法であるケースはいくつかありますが、私はこれがその一つだとは思わないのです。

Answerの代わりにArgumentCaptorを使用します。私はこのようなメソッドをテストクラスに書いて、 "Hello world"を引数として呼び出します。私はこれをテストしていないので、それはタイプミスを含んでいるかもしれないことに注意してください。

private void verifyCharactersWritten(String expected){ 
    ArgumentCaptor<Character> captor = ArgumentCaptor.forClass(Character.class); 
    verify(mockWriter, times(expected.length())).write(captor.capture()); 
    assertEquals(Arrays.asList(expected.toCharArray()), captor.getAllValues()); 
} 

これが役に立ちます。

+0

スリック。私はargキャプチャのその機能について知らなかった。 –

+0

結果/出力/結果(可能な場合)を簡単にチェックし、このテストコードを読むことがあります。私たちがそれを返すべきであることを知っていれば、与えられた議論のために「期待していた」と言いました。 – ses

4

検証バグとして提出が行われた回数何かとは別の概念であり、それことを確認します起こったのは、イン・オーダー・チェックをパスしますが、 'l'コールが3回行われたために失敗し、あなたは(暗黙のうちに)それを1回だけ期待するように言いました。それは私がMockitoで以前にヒットした奇妙なことだが、それが起こるといつも、私はテストがうまく書かれていないと判断してしまい、問題を解決すると問題はなくなる。あなたの場合は、私はそれが方法ライターに書かれた各文字を確認するために過剰殺到だと思います。メッセージが正しく送信されたことを確認するには、入力メッセージと出力メッセージを比較する必要があります。あなたの例では、ライターを嘲笑するのではなく、StringWriterを使う必要があります。あなたが本当に何をやっているしなければならない場合は、あなたのテストの最後には、ちょうど私がお勧めすることができ、すべてがそれを実現するための方法がありますかどうかを確認するためにMockitoコードに掘り、おそらくされ、

assertThat(stringWriter.toString(), equalTo(message)); 

のように見えます彼らがそれについて何を言うかを見るためにバグレポートを提出する。

+0

I/Oは、に1文字ずつ起こるので、私は、仮想マシン/インタプリタを書いています時間は、メッセージ全体の概念はなく、ただの一連の個々の文字が標準出力に印刷されています。私はそれらを印刷する過程で何かを壊すことはないようにしようとしています(はい、自分の内部メモリを実装しています)。私が確認しようとしているメッセージは "Hello World"なので、そのようなメッセージを文字単位で正しく印刷できることをテストすることは難しいとは思わない。 :) – ArtB

+1

こんにちは、このテストが重要である場合、つまり、あなたが何を出すのかテストする必要があります。私は@ライアンに同意します。入力と出力の比較を優先すべきです。作者をスタブするときは、通常の 'StringBuilder'にcharを追加するカスタム回答を使用し、それを私たちの' 'Hello World" '入力と比較してください。あるいはカスタムマッチャーを書くこともできます。文字列が変更された場合、このテストは簡単に破損する可能性があります!** – Brice

+0

@Briceよくコードスニペット(例えば、文字列が変更された場合)エミュレートされている/解釈されているのはテスト入力の一部なので、変更するとテストが失敗することが予想されます。 – ArtB

0

私は現在、カスタムアンサーでこれをハッキングしています。

final List<Integer> writtenChars = new ArrayList<>(); 
willAnswer(
     new Answer(){ 
      @Override 
      public Object answer(final InvocationOnMock invocation)throws Throwable { 
       final int arg = (int) invocation.getArguments()[0]; 
       writtenChars.add(arg); 
       return null; 
      } 
     } 
    ).given(mockWriter).write(anyInt()); 

次に、希望するメソッドを実行した後、リストに対して期待される文字列に対してテストを行います。ブライスあなたに謝罪:このものの

final Iterator<Integer> writtenCharItr = writtenChars.iterator(); 
for(int charInt : "Hello World!\n".toCharArray()) 
    assertThat( charInt, is(writtenCharItr.next()) ); 
assertThat("There are no more chars.", writtenCharItr.hasNext(), is(false)); 
verify(mockWriter).flush(); 

は、あなたがなど


EDITメソッドが呼ばれましリストに記録しない限り、あなたはメソッド呼び出し回以上に興味を持っている場合は動作しません。 Listの代わりにStringBuilderを使用することで、独立して優れている場合を除き、このソリューションに独自に来ているようですが、一般的なケースではListがうまく機能します。

+0

私は何を考えていた... ArgumentCaptorは良い方法です、それは基本的にこのカスタム回答がやっていることです。デイヴィッドの対応はいろいろな点でずっと正確です。 – Brice

2

このように動作する理由は、確認と定期的な確認の整合性です。言い換えれば、この方法を実装しなかった場合、APIは別の方法で驚くべきことでした。まともなAPIを設計しようとするときにトレードオフを行います。

だから...答え。まず、テストコード内のループ(または条件文)のようなステートメントを避ける必要があります。その理由は、テストコードの明快さと保守性の面で気をつけているからです! =)

テストからループを削除しても、ユースケースはありません。ユースケースがなければ、答えは出せません。 DavidのArgumentCaptorは悪い考えではないかもしれません。

希望に役立ちます!

+0

私は同意しません。私はループを展開することができ、同じ問題があります。私はこれをデバッグしようとしたときにそうしたように、これを知っています。第二に、それがInOrderを呼び出すが、呼び出しがどのような順序で行われたのかが分からないのであれば、それほど驚くべきことではないという暴力的な違反ではないか? – ArtB

0

これは奇妙なテストですが、依然としてmocking APIでサポートされている必要があります。私はそれを信じるは、他の模擬APIがそれをサポートしているので、Mockitoによってサポートされると信じています。 Unitilsモック

Mock<Writer> mockWriter; 

@Test 
public void inOrderTest() throws Exception { 
    Writer writer = mockWriter.getMock(); 
    final String message = "Hello World!\n"; 

    for (char c : message.toCharArray()) 
     writer.write(c); 

    for (char c : message.toUpperCase().toCharArray()) 
     mockWriter.assertInvokedInSequence().write(c); 
    MockUnitils.assertNoMoreInvocations(); 
} 

それともJMockit(私の独自のツール)を持つ:

@Test 
public void inOrderTest(final Writer mockWriter) throws Exception { 
    final String message = "Hello World!\n"; 

    for (char c : message.toCharArray()) 
     mockWriter.write(c); 

    new FullVerificationsInOrder() {{ 
     for (char c : message.toCharArray()) 
      mockWriter.write(c); 
    }}; 
} 
関連する問題