BDDMockito 快速指南,你學(xué)會了嗎?
概述
BDD一詞最早由Dan North于2006年創(chuàng)造。BDD鼓勵用自然的、可讀的語言編寫測試,重點(diǎn)關(guān)注應(yīng)用程序的行為。
它定義了一種結(jié)構(gòu)清晰的測試編寫方式,分為三個部分(排列、動作、斷言):
- 給定一些先決條件(排列)
- 行動發(fā)生時(行動)
- 然后驗(yàn)證輸出(Assert)
Maven依賴項(xiàng):
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.21.0</version>
</dependency>
包含以下靜態(tài)導(dǎo)入,我們的測試可以變得更可讀:
import static org.mockito.BDDMockito.*;
BDDMockito擴(kuò)展了Mockito,所以我們不會錯過傳統(tǒng)Mockito API提供的任何功能。
Mockito與BDDMockito
Mockito傳統(tǒng)mock是在排列步驟中使用when(obj).then*()執(zhí)行的,稍后,可以在Assert步驟中使用verify()驗(yàn)證與mock的交互。
BDDMockito為各種Mockito方法提供了BDD別名,因此我們可以使用given(而不是when)編寫Arrange步驟,同樣,我們可以使用then(而不是verify)編寫Assert步驟。
讓我們看一個使用傳統(tǒng)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允許我們返回一個可能是固定的或動態(tài)的值。它還允許我們拋出一個異常:
- 返回固定值
使用BDDMockito,我們可以很容易地將Mockito配置為在調(diào)用mock對象目標(biāo)方法時返回固定結(jié)果:
given(phoneBookRepository.contains(momContactName))
.willReturn(false);
phoneBookService.register(xContactName, "");
then(phoneBookRepository)
.should(never())
.insert(momContactName, momPhoneNumber);
- 返回動態(tài)值
BDDMockito允許我們提供一種更復(fù)雜的返回值的方法。我們可以根據(jù)輸入返回一個動態(tài)結(jié)果:
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);
- 引發(fā)異常
告訴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的位置,這是強(qiáng)制性的,以防我們mock一個沒有返回值的方法。
還要注意,我們使用了(any,eq)等參數(shù)匹配器,以提供一種更通用的基于標(biāo)準(zhǔn)而非固定值的mock方式。
結(jié)論
我們討論了BDDMockito如何試圖將BDD特性引入Mockito測試,并討論了Mockito和BDDMockito之間的一些差異。