如何将Mockito捕获的参数传递给注入的模拟对象的方法?

时间:2015-03-20 15:08:12

标签: java spring unit-testing mockito

我正在尝试测试一个服务类,它在内部使用Spring AMQP连接对象。此连接对象由Spring注入。但是,我不希望我的单元测试实际上与AMQP代理通信,因此我使用Mockito注入连接对象的模拟。

/** 
 * The real service class being tested.  Has an injected dependency. 
 */ 
public class UserService {

   @Autowired
   private AmqpTemplate amqpTemplate;

   public final String doSomething(final String inputString) {
      final String requestId = UUID.randomUUID().toString();
      final Message message = ...;
      amqpTemplate.send(requestId, message);
      return requestId;
   }
}

/** 
 * Unit test 
 */
public class UserServiceTest {

   /** This is the class whose real code I want to test */
   @InjectMocks
   private UserService userService;

   /** This is a dependency of the real class, that I wish to override with a mock */
   @Mock
   private AmqpTemplate amqpTemplateMock;

   @Before
   public void initMocks() {
      MockitoAnnotations.initMocks(this);
   }

   @Test
   public void testDoSomething() {
      doNothing().when(amqpTemplateMock).send(anyString(), any(Message.class));

      // Call the real service class method, which internally will make 
      // use of the mock (I've verified that this works right).
      userService.doSomething(...);

      // Okay, now I need to verify that UUID string returned by 
      // "userService.doSomething(...) matches the argument that method 
      // internally passed to "amqpTemplateMock.send(...)".  Up here 
      // at the unit test level, how can I capture the arguments passed 
      // to that inject mock for comparison?
      //
      // Since the value being compared is a UUID string created 
      // internally within "userService", I cannot just verify against 
      // a fixed expected value.  The UUID will by definition always be
      // unique.
   }
}

此代码示例中的注释有望清楚地列出问题。当Mockito将一个模拟依赖注入到一个真正的类中,并且对真实类的单元测试导致它调用mock时,你怎么能在以后检索传递给注入模拟的确切参数?

2 个答案:

答案 0 :(得分:65)

使用一个或多个ArgumentCaptor s。

目前还不清楚你的类型是什么,但无论如何。假设你有一个模拟,其方法doSomething()Foo为参数,然后你这样做:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class);

verify(mock).doSomething(captor.capture());

final Foo argument = captor.getValue();

// Test the argument

此外,看起来您的方法返回void并且您不希望它执行任何操作。写下这个:

doNothing().when(theMock).doSomething(any());

答案 1 :(得分:8)

您可以将doAnswer()挂钩到send()上的amqpTemplateMock方法的存根,然后捕获AmqpTemplate.send()的调用参数。

testDoSomething()的第一行设为此

    Mockito.doAnswer(new Answer<Void>() {
          @Override
          public Void answer(final InvocationOnMock invocation) {
            final Object[] args = invocation.getArguments();
            System.out.println("UUID=" + args[0]);  // do your assertions here
            return null;
          }
    }).when(amqpTemplateMock).send(Matchers.anyString(), Matchers.anyObject());

将所有内容放在一起,测试成为

import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class UserServiceTest {

  /** This is the class whose real code I want to test */
  @InjectMocks
  private UserService userService;

  /** This is a dependency of the real class, that I wish to override with a mock */
  @Mock
  private AmqpTemplate amqpTemplateMock;

  @Before
  public void initMocks() {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void testDoSomething() throws Exception {
    Mockito.doAnswer(new Answer<Void>() {
      @Override
      public Void answer(final InvocationOnMock invocation) {
        final Object[] args = invocation.getArguments();
        System.out.println("UUID=" + args[0]);  // do your assertions here
        return null;
      }
    }).when(amqpTemplateMock).send(Matchers.anyString(), Matchers.anyObject());
    userService.doSomething(Long.toString(System.currentTimeMillis()));
  }
}

这给出了输出

  

UUID = 8e276a73-12fa-4a7e-a​​7cc-488d1ce0291f

我通过阅读这篇文章找到了这个, How to make mock to void methods with mockito