BDDMockito 快速指南,你学会了吗?
概述
BDD一词最早由Dan North于2006年创造。BDD鼓励用自然的、可读的语言编写测试,重点关注应用程序的行为。
它定义了一种结构清晰的测试编写方式,分为三个部分(排列、动作、断言):
- 给定一些先决条件(排列)
- 行动发生时(行动)
- 然后验证输出(Assert)
Maven依赖项:
org.mockito mockito-core 2.21.0
包含以下静态导入,我们的测试可以变得更可读:
import static org.mockito.BDDMockito.*;
BDDMockito扩展了Mockito,所以我们不会错过传统Mockito API提供的任何功能。
Mockito与BDDMockito
Mockito传统mock是在排列步骤中使用when(obj).then*()执行的,稍后,可以在Assert步骤中使用verify()验证与mock的交互。
BDDMockito为各种Mockito方法提供了BDD别名,因此我们可以使用given(而不是when)编写Arrange步骤,同样,我们可以使用then(而不是verify)编写Assert步骤。
让我们看一个使用传统Mockito的测试体示例:
when(phoneBookRepository.contains(momContactName)) .thenReturn(false); phoneBookService.register(momContactName, momPhoneNumber); verify(phoneBookRepository) .insert(momContactName, momPhoneNumber);
让我们看看与BDDMockito相比如何:
given(phoneBookRepository.contains(momContactName)) .willReturn(false); phoneBookService.register(momContactName, momPhoneNumber); then(phoneBookRepository) .should() .insert(momContactName, momPhoneNumber);
BDDMockito Mock
让我们试着测试PhoneBookService,我们需要模拟PhoneBookRepository:
public class PhoneBookService { private PhoneBookRepository phoneBookRepository; public void register(String name, String phone) { if(!name.isEmpty() && !phone.isEmpty() && !phoneBookRepository.contains(name)) { phoneBookRepository.insert(name, phone); } } public String search(String name) { if(!name.isEmpty() && phoneBookRepository.contains(name)) { return phoneBookRepository.getPhoneNumberByContactName(name); } return null; } }
BDDMockito作为Mockito允许我们返回一个可能是固定的或动态的值。它还允许我们抛出一个异常:
- 返回固定值
使用BDDMockito,我们可以很容易地将Mockito配置为在调用mock对象目标方法时返回固定结果:
given(phoneBookRepository.contains(momContactName)) .willReturn(false); phoneBookService.register(xContactName, ""); then(phoneBookRepository) .should(never()) .insert(momContactName, momPhoneNumber);
- 返回动态值
BDDMockito允许我们提供一种更复杂的返回值的方法。我们可以根据输入返回一个动态结果:
given(phoneBookRepository.contains(momContactName)) .willReturn(true); given(phoneBookRepository.getPhoneNumberByContactName(momContactName)) .will((InvocationOnMock invocation) -> invocation.getArgument(0).equals(momContactName) ? momPhoneNumber : null); phoneBookService.search(momContactName); then(phoneBookRepository) .should() .getPhoneNumberByContactName(momContactName);
- 引发异常
告诉Mockito抛出异常非常简单:
given(phoneBookRepository.contains(xContactName)) .willReturn(false); willThrow(new RuntimeException()) .given(phoneBookRepository) .insert(any(String.class), eq(tooLongPhoneNumber)); try { phoneBookService.register(xContactName, tooLongPhoneNumber); fail("Should throw exception"); } catch (RuntimeException ex) { } then(phoneBookRepository) .should(never()) .insert(momContactName, tooLongPhoneNumber);
注意我们是如何交换given和will的位置,这是强制性的,以防我们mock一个没有返回值的方法。
还要注意,我们使用了(any,eq)等参数匹配器,以提供一种更通用的基于标准而非固定值的mock方式。
结论
我们讨论了BDDMockito如何试图将BDD特性引入Mockito测试,并讨论了Mockito和BDDMockito之间的一些差异。