三种mock注入方式了解一下

在前一篇文章中,简要介绍了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实现了

  1. @Mock将外部依赖StockService 进行了mock
  2. @InjectMocks通过调用Portfolio类的无参构造方法完成了portfolio的实例化,并通过Portfolio类提供的setStockService()方法,用setter注入的方式,将前述被mock的stockService注入进portfolio。

mock之构造方法注入

那么问题来了,一定是需要写了setter方法才能将Mock注入么?

来看一下

public class Portfolio {      
private StockService stockService;  
private List 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我们发现,

image.png

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

image.png

mock之属性注入

最后,我们将带参的构造方法和setter都注释掉,再增加一个无参的构造方法。

public class Portfolio {      
private StockService stockService;  
private List 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也可以注入。

image.png

如果我们定义了两个同类型的变量,

private StockService stockService;  
private StockService stockService2;

那么可以通过指定name来让Mockito选择注入哪一个。

@Mock(name="stockService2")  
StockService stockService;

Debug可以看到

image.png

由于我们故意让Mockito注入到stockService2上,所以原先stockService就变成了null,也就是用例会失败。

最后,我们来总结一下

  • 1、注入方式的选择顺序:Mockito 尝试按 非默认构造方法, setter 方法, 属性 的顺序来注入 Mock 对象。如果存在一个带参的构造方法,那么 setter 方法 和 属性 注入都不会发生。
  • 2、setter方法注入: Mockito 首先根据属性类型找到 Mock 对象。存在多个相同类型 Mock 对象则按名称(@Mock(name="stockService"))进行匹配,默认名称为空。
  • 3、属性注入: 按 Mock 对象的类型或是名称的匹配规则与 setter 方法注入 是一样的。

参考

image.png

你可能感兴趣的:(三种mock注入方式了解一下)