首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >讲真,居然有三种mock注入方式?

讲真,居然有三种mock注入方式?

作者头像
Antony
发布2020-12-01 18:04:59
发布2020-12-01 18:04:59
5.6K0
举报

前一篇文章中,简要介绍了Mockito的引入和使用。本篇来介绍一下Mockito的三种mock注入方式。

1-Setter注入

在之前的案例中,笔者介绍了如何利用Mockito的mock方法来解决被测代码的外物依赖。随着基于注解的开发方式的流行,Mockito也提供了注解的方式来实现对依赖的打桩以及注入,也就是@Mock和@InjectMocks注解。

如果使用这两个注解对前述案例进行重构后,修改部分的代码如下。

代码语言:javascript
复制
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的初始化工作。而以下的三行代码不再需要了。

代码语言:javascript
复制
   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。

2-构造方法注入

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

来看一下

代码语言:javascript
复制
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我们发现,

image.png

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

3-属性注入

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

代码语言:javascript
复制
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也可以注入。

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

代码语言:javascript
复制
private StockService stockService;
private StockService stockService2;

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

代码语言:javascript
复制
@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 方法注入 是一样的。

本文参考

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 软件测试那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1-Setter注入
  • 2-构造方法注入
  • 3-属性注入
  • 最后,我们来总结一下
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档