Mockitoで期待する方法 -- java フィールド と unit-testing フィールド と mockito フィールド 関連 問題

How to set an expect in Mockito?












2
vote

問題

日本語

テストするコードがあるとします

<事前> <コード> void myMethod() { byte []data = new byte[1]; data[0]='a'; output.send(42, data); data[0]='b'; output.send(55, data); }

テストを書く:

<事前> <コード> testSubject.myMethod(); verify(output).send(eq(42), aryEq(new byte[]{'a'})); verify(output).send(eq(55), aryEq(new byte[]{'b'}));

メソッドの実装が両方の呼び出しに対して同じ配列を再利用するため、テストは失敗しますが、メソッドが終了した後で最初の<コード> send 呼び出しの引数を一致させることは不可能です。メソッド呼び出し、期待のようなもの。

そのような方法をテストする正しい方法は何ですか?

英語

Suppose I have a code to test

void myMethod() {   byte []data = new byte[1];   data[0]='a';   output.send(42, data);   data[0]='b';   output.send(55, data); } 

I write a test:

testSubject.myMethod(); verify(output).send(eq(42), aryEq(new byte[]{'a'})); verify(output).send(eq(55), aryEq(new byte[]{'b'})); 

The test will fail as the method implementation reuses the same array for both calls, it is impossible to match args of the first send invocation after method finishes, so techically the verify statements should be specified before the method call, something like an expectation.

What is the right way to test such methods?

</div
        
     
     

回答リスト

2
 
vote

Mockitoはここで少し不便なようです。メソッド呼び出しを検出してログに記録します( mock(MyOutput.class, withSettings().verboseLogging()); を使用してロギングを有効にします)、それはあなたが渡している配列への参照を保存し、したがって配列を突然切るときに影響されます。その後、メソッド呼び出しは<コード> send(42, [97]) ではなく<コード> send(42, [98]) であったと考えています。

それを扱う可能性のある方法はあなたが言及した期待を使うことです。たとえば、カウンタを使用して、通話が期待値に一致した場合は増加することができます(本当にだけでなく、かなり厄介な):

<事前> <コード> MyOutput mock = mock(MyOutput.class, withSettings().verboseLogging()); Main subject = new Main(mock); AtomicInteger correctCallsCounter = new AtomicInteger(0); doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(42), aryEq(new byte[]{'a'})); doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(55), aryEq(new byte[]{'b'})); subject.myMethod(); assertThat(correctCallsCounter.get(), is(2));

doAnswer は、通話が発生したとき、およびバイト配列がまだ変更されていないときにトリガされているためです。

この回避策の大きな側面は、 void メソッドでのみ機能します。 send6 が「何か」を返す場合、現在それを回避する方法はありません。
まあ、もう一つはこれが明らかにかなり厄介な回避策であるということです。

だから私はあなたのコードを少し少しリファクタリングすることを提案します(新しい配列を使ってください)。それはここでこれらの問題を回避するでしょう。


send メソッドが実際に何かを返し、<コード> myMethod メソッドがそれに依存している場合は、通常これをモックすることができます(<コード> send この例では文字列を返すと予想されます):

<事前> <コード> send(42, [98])0

上記の回避策を使用するには、 send(42, [98])1 メソッドを変更してカウンタをインクリメントし、文字列を返します(とにかくモックするため、それはそれほど悪くない)。

<事前> <コード> send(42, [98])2
 

Well it looks like Mockito is a bit inconvenient here. It detects the method call and logs it (use mock(MyOutput.class, withSettings().verboseLogging()); to enable logging), but it stores the reference to the array you're passing and thus is influenced when you mutate the array. It then thinks the method call was send(42, [98]) rather than send(42, [97]).

A possible way to work with that is to use the expectations you've mentioned. For example you can use a counter and increment it if a call matched the expectations (it is really just a workaround and rather nasty):

MyOutput mock = mock(MyOutput.class, withSettings().verboseLogging()); Main subject = new Main(mock); AtomicInteger correctCallsCounter = new AtomicInteger(0); doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(42), aryEq(new byte[]{'a'})); doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(55), aryEq(new byte[]{'b'}));  subject.myMethod();  assertThat(correctCallsCounter.get(), is(2)); 

It works, because doAnswer is triggered when the call happens and when the byte array hasn't been changed yet.

The big downside of this workaround is, that it only works with void methods. If send would return "something", then I currently don't see a way to work around that.
Well and the other one is that this is obviously a rather nasty workaround.

So I would suggest to refactor your code a bit (use a new array) if that is possible. That would avoid these issues here.


If your send method would indeed return something and your myMethod method would rely on it, then you would usually mock it this way (send is expected to return a String in this example):

when(mock.send(eq(55), aryEq(new byte[]{'b'}))).thenReturn("something"); 

In order to still use the above mentioned workaround, you could change the doAnswer method to increment your counter and return your String (which you would mock anyway, thus it isn't that bad):

doAnswer(invocation -> {     correctCallsCounter.incrementAndGet();     return "something"; }).when(mock).send(eq(42), aryEq(new byte[]{'a'})); 
</div
 
 
   
   
0
 
vote

send(42, [98])3 を使用して、パラメータの値をコピーします。 これがいくつかのコードです(それはかわいいではありません):

<事前> <コード> send(42, [98])4

パラメータの値は呼び出しの検証の分別を比較しますが、パラメータの順序はまだ検証されます(すなわち、最初に、次に 'B'アレイ)。

 

Use an Answer to copy the value of the parameter. Here is some code (it is not pretty):

public class TestMyClass {     private static List<byte[]> mockDataList = new ArrayList<>();      @InjectMocks     private MyClass classToTest;      private InOrder inOrder;      @Mock     private ObjectClass mockOutputClass;      @After     public void afterTest()     {         inOrder.verifyNoMoreInteractions();          verifyNoMoreInteractions(mockOutputClass);     }      @Before     public void beforeTest()     {         MockitoAnnotations.initMocks(this);          doAnswer(new Answer()         {             @Override             public Object answer(                 final InvocationOnMock invocation)                     throws Throwable             {                 final byte[] copy;                 final byte[] source = invocation.getArgument(1);                  copy = new byte[source.length];                 System.arraycopy(source, 0, copy, 0, source.length);                  mockDataList.add(copy);                  return null;             }         }).when(mockOutputClass).send(anyInt(), any(byte[].class));;           inOrder = inOrder(             mockOutputClass);     }      @Test     public void myMethod_success()     {         byte[] actualParameter;         final byte[] expectedFirstArray = { (byte)'a' };         final byte[] expectedSecondArray = { (byte)'b' };           classToTest.myMethod();           actualParameter = mockDataList.get(0);         assertArrayEquals(             expectedFirstArray,             actualParameter);          inOrder.verify(mockOutputClass).send(eq(42), any(byte[].class));           actualParameter = mockDataList.get(1);         assertArrayEquals(             expectedSecondArray,             actualParameter);          inOrder.verify(mockOutputClass).send(eq(55), any(byte[].class));     } } 

Note that the value of the parameter is compared separate of the verification of the call, but the order of the parameters is still verified (i.e. 'a' array first, then 'b' array).

</div
 
 
     
     

関連する質問

4  mockito:文字列とリスト<string>引数を一致させる方法  ( Mockito how to match a string and liststring arguments ) 
基本的には、メソッドで正しいパラメータが呼び出されているかどうかを確認しようとしています。 これは私がテストしようとしているコードの範囲です: <事前> <コード> Criteria criteria = session.createCriteria(User...

58  Mockitoを使用したローカルスコープオブジェクトのモッキング方法  ( Mocking methods of local scope objects with mockito ) 
これでいくつかの助けが必要です: 例: <事前> <コード> void method1{ MyObject obj1=new MyObject(); obj1.method1(); } テストで $start0 をMOCK にしたいが、透明...

2  Mockitoパッケージプライベートスーパークラスに実装された方法をスタブメソッドを実行しようとすると実際のメソッドを呼び出す  ( Mockito calling real methods when attempting to stub methods implemented on a pa ) 
これはこの質問しかし、再現がはるかに簡単です。 問題は、インターフェイスメソッド実装を持つパッケージプライベートクラスを拡張するモックパブリッククラスにインタフェースメソッドをスタブする場合です。 Mockitoは、パッケージプライベートクラスの実際のインター...

1  Mockito 1.9.5でjdbctemplate.QueryForObject(.. ...、...、)をモッキングする  ( Mocking jdbctemplate queryforobject with mockito 1 9 5 ) 
私の質問はこれで質問 私は2つの JdbcTemplate.queryForObject(..,...,...,) を持っています。 <事前> <コード> depositPostedAmt = jdbcTemplate.queryForObject(Con...

20  voidメソッドをテストするためのMockito  ( Mockito to test void methods ) 
テストしたいコードを次のコードがあります。 <事前> <コード> public class MessageService { private MessageDAO dao; public void acceptFromOffice(Messa...

5  アンドロイド。ロボレクタ。アカウントマニュアル結果のテスト  ( Android robolectric testing accountmanager results ) 
私のアプリケーションでは、所有者の電子メールを受けるためにアカウントマネージャを使用してメソッドを使用します。どのようにしてこの方法をRobolectricでテストできますか?私はこの目的のモッキングのために使うべきですか?私が正しいなら、Mockitoを使う...

3  Mockito SpiedInstanceが壊れていますか(スパイと比較して)、またはDoc誤解を招くのですか?  ( Is mockito spiedinstance broken compared to spy or is the doc misleading ) 
MockSettingsの現在のMockitoドキュメントページには、このが含まれています <事前> <コード> Foo foo = mock(Foo.class, withSettings().spiedInstance(fooInstance)); //...

0  unull null null checkを変更する方法  ( How to string not null check in junit ) 
私はコード後の単位テストを書いています。しかし、カバレッジは以下のコード行に欠けています。私たちはどのようにして下記の行をカバーすることができるのかわからない。私の研究は助けをしませんでした。 <事前> <コード> fatal error: unexp...

5  Mocking Clojureプロトコル  ( Mocking clojure protocols ) 
easymock または mockito 9988777662 で定義されたClojureプロトコルをMock Mock Mock Mockに使用するために使用されますか?もしそうなら、? ...

4  mockito:制御フローを通してモックを注入する  ( Mockito injecting mocks throughout control flow ) 
私はまだMockitoを学んでいて、今はモックを注入する方法を学んでいます。 私は他のオブジェクトに依存する特定の方法でテスト中のオブジェクトを持っています。これらのオブジェクトは、他のオブジェクトに依存します。私は特定のことを嘲笑し、実行中のどちらの範囲でも...




© 2022 cndgn.com All Rights Reserved. Q&Aハウス 全著作権所有