MockitoとJUnitを使用してマルチスレッドアプリケーションをテストしようとしています。ここで問題のコードの一部です:Mockito ArgumentCaptorが多数の繰り返しレコードを取得しています
ArgumentCaptor<MessageCreator> messageCaptor = ArgumentCaptor.forClass(MessageCreator.class);
jmsHandler.put(message);
Mockito.verify(mockJmsTemplate, Mockito.after(10000).times(4)).send(messageCaptor.capture());
jmsHandler.put(message)
ラインはBlockingQueue
になり、マルチスレッドの部分で始まるアプリケーションへString
を置きます。次に、メソッドが次の10秒間に4回実行され、結果が取得されるのを待ちます。
アプリケーションはMessageCreator
の4つのインスタンスを吐き出す必要があります。これは私の目的では、予期した出力と比較するString
を含むオブジェクトに過ぎません。テスト中のログにより、4つのメッセージが作成されていることが確認されます。私は、結果を確認するためにArgumentCaptor
のgetAllValues()
方法をループしてみました
は、しかし、私はList
は文字通り数百万回を私のプログラムの出力を複製していることに気づきました。それには4つのオブジェクトが必要ですが、最後の実行には6,984,988があります。
この数値は多少の変動はあるようですが、デバッグモードでテストタイミングを巡って大きく変化します。たとえば、jmsHandler
行にブレークポイントを設定し、ステップを進めて、Mockito.verify(...)
ステップを開始する前にアプリケーションが処理を完了するのを待つと、List
のサイズは "単なる" 158,636オブジェクトに激減します。
誰かがこの種の問題に遭遇したことはありますか?これ以上の詳細をご提供できない場合は、私にお知らせください。
編集:ここでは、テストとプログラム構造の自己完結型の例です:
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
public class MultiThreadTest {
private Input inputHandler;
@Mock
private JmsTemplate mockJmsTemplate;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);
inputHandler = new Input();
inputHandler.setQueue(queue);
Output outputHandler = new Output();
outputHandler.setQueue(queue);
outputHandler.setJmsTemplate(mockJmsTemplate);
new Thread(outputHandler).start();
}
@Test
public void testMessage() {
ArgumentCaptor<OutputMessageCreator> messageCaptor = ArgumentCaptor.forClass(OutputMessageCreator.class);
String inMessage = "testMessage";
List<String> expectedMessages = new ArrayList<String>(4);
inputHandler.put(inMessage);
Mockito.verify(mockJmsTemplate, Mockito.after(10000).times(4)).send(messageCaptor.capture());
System.out.println("Number: " + messageCaptor.getAllValues().size());
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(0)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(1)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(2)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(3)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(4)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(5)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(6)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(7)));
System.out.println("Equal: " + messageCaptor.getAllValues().get(0).equals(messageCaptor.getAllValues().get(8)));
List<String> outMsgs = new ArrayList<String>();
for (OutputMessageCreator creator : messageCaptor.getAllValues()) {
outMsgs.add(creator.getMsg());
}
assertEquals(expectedMessages, outMsgs);
}
private class Input {
private BlockingQueue<String> queue;
public void put(String msg) {
try {
queue.put(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void setQueue(BlockingQueue<String> queue) {
this.queue = queue;
}
}
private class Output implements Runnable {
private BlockingQueue<String> queue;
private JmsTemplate jmsTemplate;
private int counter = 1;
@Override
public void run() {
while (true) {
String msg = null;
try {
msg = queue.take();
String[] messagesOut = new String[4];
for (int i = 0; i < 4; i++) {
messagesOut[i] = msg + "-" + counter++;
}
for (String messageOut : messagesOut) {
System.out.println(messageOut);
jmsTemplate.send(new OutputMessageCreator(messageOut));
Thread.sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void setQueue(BlockingQueue<String> queue) {
this.queue = queue;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
}
private class OutputMessageCreator implements MessageCreator {
private String msg;
public OutputMessageCreator(String msg) {
this.msg = msg;
}
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage();
message.setText(msg);
return message;
}
public String getMsg() {
return msg;
}
}
}
は、ここでそのテストを実行しているからの出力です:
testMessage-1
testMessage-2
testMessage-3
testMessage-4
Number: 5392168
Equal: true
Equal: false
Equal: false
Equal: false
Equal: true
Equal: false
Equal: false
Equal: false
Equal: true
問題はArgumentCaptorなしで発生しますか?はいの場合は、タイトルを変更することができます。いずれにしても、実際の例を提示することができます。 – jdebon
@jdebonフィードバックいただきありがとうございます。問題は 'ArgumentCaptor'です。テスト自体は期待通りに4つのメッセージしか作成しませんが、 'ArgumentCaptor'は何百万回も繰り返しキャプチャしています。つまり、 'getAllValues()'は[msg1、msg2、msg3、msg4、msg1、msg2、msg3、msg4、msg1、...]を返します。私は元の質問に問題のレクリエーションを付けました。 – ryoung10