在前一篇文章中,简要介绍了Mockito的引入和使用。本篇来介绍一下Mockito的三种mock注入方式。
使用@Mock/@InjectMocks注解
在之前的案例中,笔者介绍了如何利用Mockito的mock方法来解决被测代码的外物依赖。随着基于注解的开发方式的流行,Mockito也提供了注解的方式来实现对依赖的打桩以及注入,也就是@Mock和@InjectMocks注解。
如果使用这两个注解对前述案例进行重构后,修改部分的代码如下。
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);
}
}
对比之前的代码,可以发现在原来测试类的成员变量定义中,分别在stockService和portfolio前添加了@Mock和@InjectMocks的注解。
-
@Mock注解:Mockito 通过 @mock 注解来创建 mock 对象,可以用来代替Mockito.mock()方法。
-
@InjectMocks:创建一个实例,并将@Mock(或@Spy)注解创建的mock注入到用该实例中。
和之前的代码相比,在使用了这两个注解之后,setup()方法也发生了变化。额外增加了以下这样一行代码。
MockitoAnnotations.*initMocks*(this);
也就是实现了对上述mock的初始化工作。而以下的三行代码不再需要了。
public void setUp(){
portfolio = new Portfolio();
stockService = mock(StockService.class);
portfolio.setStockService(stockService);
}
也就是说,通过@Mock/@InjectMocks 以及initMocks方法的配合,Mockito实现了
- @Mock将外部依赖StockService 进行了mock
- @InjectMocks通过调用Portfolio类的无参构造方法完成了portfolio的实例化,并通过Portfolio类提供的setStockService()方法,用setter注入的方式,将前述被mock的stockService注入进portfolio。
mock之构造方法注入
那么问题来了,一定是需要写了setter方法才能将Mock注入么?
来看一下
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进行一下重构,新增加了一个带参的构造方法Portfolio(StockService stockService)。
还是执行之前的用例,通过Debug我们发现,

stockService已经通过构造注入的方式,Mockito利用上述带参的构造方法将被mock的stockService注入到了portfolio之中。所以,测试用例依旧是可以通过的,并且从打印内容上看,也的确是带参的构造方法被调用了,并且优先级还在setter方法之前。

mock之属性注入
最后,我们将带参的构造方法和setter都注释掉,再增加一个无参的构造方法。
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;
// }
}
我们可以发现,Mockito调用了Portfolio类的无参构造方法为portfolio进行了实例化,并且在这个过程顺利地将StockService进行了mock,注入到了portfolio中的stockService变量。也就是所谓的通过属性注入的方式。也可以看到,即使是私有的变量Mockito也可以注入。

如果我们定义了两个同类型的变量,
private StockService stockService;
private StockService stockService2;
那么可以通过指定name来让Mockito选择注入哪一个。
@Mock(name="stockService2")
StockService stockService;
Debug可以看到

由于我们故意让Mockito注入到stockService2上,所以原先stockService就变成了null,也就是用例会失败。
最后,我们来总结一下
- 1、注入方式的选择顺序:Mockito 尝试按 非默认构造方法, setter 方法, 属性 的顺序来注入 Mock 对象。如果存在一个带参的构造方法,那么 setter 方法 和 属性 注入都不会发生。
- 2、setter方法注入: Mockito 首先根据属性类型找到 Mock 对象。存在多个相同类型 Mock 对象则按名称(@Mock(name="stockService"))进行匹配,默认名称为空。
- 3、属性注入: 按 Mock 对象的类型或是名称的匹配规则与 setter 方法注入 是一样的。

网友评论