Mockito 如何捕获传递给注入的模拟对象方法的参数?
How can Mockito capture arguments passed to an injected mock object's methods?
我正在尝试测试服务 class,它在内部使用 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 将模拟依赖项注入真实 class,并且对真实 class 进行单元测试导致它调用模拟时,您稍后如何检索传递给注入模拟的确切参数?
使用一个或多个 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
此外,您的方法 returns 似乎无效,您不希望它执行任何操作。就这么写:
doNothing().when(theMock).doSomething(any());
您可以将 doAnswer()
挂接到 amqpTemplateMock
上的 send()
方法的存根,然后捕获 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-a7cc-488d1ce0291f
我通过阅读这篇文章发现了这个 post,
How to make mock to void methods with mockito
我正在尝试测试服务 class,它在内部使用 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 将模拟依赖项注入真实 class,并且对真实 class 进行单元测试导致它调用模拟时,您稍后如何检索传递给注入模拟的确切参数?
使用一个或多个 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
此外,您的方法 returns 似乎无效,您不希望它执行任何操作。就这么写:
doNothing().when(theMock).doSomething(any());
您可以将 doAnswer()
挂接到 amqpTemplateMock
上的 send()
方法的存根,然后捕获 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-a7cc-488d1ce0291f
我通过阅读这篇文章发现了这个 post, How to make mock to void methods with mockito