在前一篇文章中蟆炊,簡(jiǎn)要介紹了Mockito的引入和使用猴伶。本篇來(lái)介紹一下Mockito的三種mock注入方式秩贰。
使用@Mock/@InjectMocks注解
在之前的案例中灰殴,筆者介紹了如何利用Mockito的mock方法來(lái)解決被測(cè)代碼的外物依賴(lài)敬特。隨著基于注解的開(kāi)發(fā)方式的流行,Mockito也提供了注解的方式來(lái)實(shí)現(xiàn)對(duì)依賴(lài)的打樁以及注入牺陶,也就是@Mock和@InjectMocks注解伟阔。
如果使用這兩個(gè)注解對(duì)前述案例進(jìn)行重構(gòu)后,修改部分的代碼如下掰伸。
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class PortfolioTest {
@InjectMocks
Portfolio portfolio ;
@Mock
StockService stockService;
@BeforeEach
public void setUp(){
MockitoAnnotations.initMocks(this);
}
}
對(duì)比之前的代碼皱炉,可以發(fā)現(xiàn)在原來(lái)測(cè)試類(lèi)的成員變量定義中,分別在stockService和portfolio前添加了@Mock和@InjectMocks的注解狮鸭。
@Mock注解:Mockito 通過(guò) @mock 注解來(lái)創(chuàng)建 mock 對(duì)象合搅,可以用來(lái)代替Mockito.mock()方法。
@InjectMocks:創(chuàng)建一個(gè)實(shí)例怕篷,并將@Mock(或@Spy)注解創(chuàng)建的mock注入到用該實(shí)例中历筝。
和之前的代碼相比,在使用了這兩個(gè)注解之后廊谓,setup()方法也發(fā)生了變化梳猪。額外增加了以下這樣一行代碼。
MockitoAnnotations.*initMocks*(this);
也就是實(shí)現(xiàn)了對(duì)上述mock的初始化工作。而以下的三行代碼不再需要了春弥。
public void setUp(){
portfolio = new Portfolio();
stockService = mock(StockService.class);
portfolio.setStockService(stockService);
}
也就是說(shuō)呛哟,通過(guò)@Mock/@InjectMocks 以及initMocks方法的配合,Mockito實(shí)現(xiàn)了
- @Mock將外部依賴(lài)StockService 進(jìn)行了mock
- @InjectMocks通過(guò)調(diào)用Portfolio類(lèi)的無(wú)參構(gòu)造方法完成了portfolio的實(shí)例化,并通過(guò)Portfolio類(lèi)提供的setStockService()方法匿沛,用setter注入的方式,將前述被mock的stockService注入進(jìn)portfolio扫责。
mock之構(gòu)造方法注入
那么問(wèn)題來(lái)了,一定是需要寫(xiě)了setter方法才能將Mock注入么逃呼?
來(lái)看一下
public class Portfolio {
private StockService stockService;
private List<Stock> stocks;
public Portfolio(StockService stockService) {
System.out.println("Constructor 1 called");
this.stockService = stockService;
}
public void setStockService(StockService stockService) {
this.stockService = stockService;
}
//...其余略
}
我們將Portfolio進(jìn)行一下重構(gòu)鳖孤,新增加了一個(gè)帶參的構(gòu)造方法Portfolio(StockService stockService)。
還是執(zhí)行之前的用例抡笼,通過(guò)Debug我們發(fā)現(xiàn)苏揣,
stockService已經(jīng)通過(guò)構(gòu)造注入的方式,Mockito利用上述帶參的構(gòu)造方法將被mock的stockService注入到了portfolio之中推姻。所以平匈,測(cè)試用例依舊是可以通過(guò)的,并且從打印內(nèi)容上看藏古,也的確是帶參的構(gòu)造方法被調(diào)用了,并且優(yōu)先級(jí)還在setter方法之前增炭。
mock之屬性注入
最后,我們將帶參的構(gòu)造方法和setter都注釋掉拧晕,再增加一個(gè)無(wú)參的構(gòu)造方法隙姿。
public class Portfolio {
private StockService stockService;
private List<Stock> stocks;
public Portfolio() {
System.out.println("Constructor 0 called");
}
// public Portfolio(StockService stockService) {
// System.out.println("Constructor 1 called");
// this.stockService = stockService;
// }
// public Portfolio(StockService stockService ,Boolean is) {
// this.stockService = stockService;
// }
}
我們可以發(fā)現(xiàn),Mockito調(diào)用了Portfolio類(lèi)的無(wú)參構(gòu)造方法為portfolio進(jìn)行了實(shí)例化厂捞,并且在這個(gè)過(guò)程順利地將StockService進(jìn)行了mock孟辑,注入到了portfolio中的stockService變量。也就是所謂的通過(guò)屬性注入的方式蔫敲。也可以看到,即使是私有的變量Mockito也可以注入炭玫。
如果我們定義了兩個(gè)同類(lèi)型的變量奈嘿,
private StockService stockService;
private StockService stockService2;
那么可以通過(guò)指定name來(lái)讓Mockito選擇注入哪一個(gè)。
@Mock(name="stockService2")
StockService stockService;
Debug可以看到
由于我們故意讓Mockito注入到stockService2上吞加,所以原先stockService就變成了null,也就是用例會(huì)失敗裙犹。
最后,我們來(lái)總結(jié)一下
- 1衔憨、注入方式的選擇順序:Mockito 嘗試按 非默認(rèn)構(gòu)造方法, setter 方法, 屬性 的順序來(lái)注入 Mock 對(duì)象叶圃。如果存在一個(gè)帶參的構(gòu)造方法,那么 setter 方法 和 屬性 注入都不會(huì)發(fā)生践图。
- 2掺冠、setter方法注入: Mockito 首先根據(jù)屬性類(lèi)型找到 Mock 對(duì)象。存在多個(gè)相同類(lèi)型 Mock 對(duì)象則按名稱(chēng)(@Mock(name="stockService"))進(jìn)行匹配码党,默認(rèn)名稱(chēng)為空德崭。
- 3斥黑、屬性注入: 按 Mock 對(duì)象的類(lèi)型或是名稱(chēng)的匹配規(guī)則與 setter 方法注入 是一樣的。