该系列文章翻译自https://www.baeldung.com/mockito-series
1.maven依赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.21.0</version>
<scope>test</scope>
</dependency>
Mockito的最新版本请参考https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.mockito%22%20AND%20a%3A%22mockito-core%22
2.参数匹配器
下面的例子中,当analyze方法接受的参数是"poppy"时,会返回"Flower"
doReturn("Flower").when(flowerService).analyze("poppy");
如果要做到,不管接受的参数是什么,都会返回"Flower",则需要用到anyString匹配器
when(flowerService.analyze(anyString())).thenReturn("Flower");
注意,当使用参数匹配器时,必须所有的参数都要用匹配器的方式,而不允许一部分参数是固定值,一部分参数试用匹配器,下面是一个错误的示例:
abstract class FlowerService {
public abstract boolean isABigFlower(String name, int petals);
}
FlowerService mock = mock(FlowerService.class);
when(mock.isABigFlower("poppy", anyInt())).thenReturn(true);
可使用eq匹配器修改如下:
when(mock.isABigFlower(eq("poppy"), anyInt())).thenReturn(true);
在使用匹配器时,有两点注意事项
- 不能将匹配器作为返回值,而需要返回精确的值
- 只能在校验或者模拟过程中使用匹配器,否则Mockito会抛出InvalidUseOfMatchersException异常。
针对第二个情况,一个错误的示例如下:
String orMatcher = or(eq("poppy"), endsWith("y"));
verify(mock).analyze(orMatcher);
可修改为:
verify(mock).analyze(or(eq("poppy"), endsWith("y")));
在上面的例子中可以看到,Mockito支持在参数匹配器上进行常规的逻辑运算,如‘not’、 ‘and’、‘or’.
3. 自定义参数匹配器
@Controller
@RequestMapping("/message")
public class MessageController {
@Autowired
private MessageService messageService;
@PostMapping
public Message createMessage (@RequestBody MessageApi messageDTO) {
Message message = new Message();
message.setText(messageDTO.getText());
message.setFrom(messageDTO.getFrom());
message.setTo(messageDTO.getTo());
message.setDate(Date.from(Instant.now()));
message.setId(UUID.randomUUID());
return messageService.deliverMessage(message);
}
}
@Service
public class MessageService {
public Message deliverMessage (Message message) {
return message;
}
}
public class Message {
private String from;
private String to;
private String text;
private Date date;
private UUID id;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
}
假设我们有一个MessageController,它的createMessage方法接受一个MessageDTO,并构造出Message对象,传递给messageService的deliverMessage().
以下是我们的校验,messageService调用了任何Message对象一次
verify(messageService, times(1)).deliverMessage(any(Message.class));
使用any匹配器无法校验Message对象内部更多的细节,比如是否和MessageDTO内部封装的data一致。自定义的参数匹配器如下:
public class MessageMatcher implements ArgumentMatcher<Message> {
private Message left;
// constructors
@Override
public boolean matches(Message right) {
return left.getFrom().equals(right.getFrom()) &&
left.getTo().equals(right.getTo()) &&
left.getText().equals(right.getText()) &&
right.getDate() != null &&
right.getId() != null;
}
}
使用自定义参数匹配器时,需要使用argThat,如下:
verify(messageService, times(1)).deliverMessage(argThat(new MessageMatcher(message)));
现在我们就可以验证传递到messageService.deliverMessageMessage()方法的mesage和MessageDTO包含相同的数据了。