深入浅出的掌握Mockito

文章目录


前言:想编写零缺陷的代码,编写测试代码很关键。最近在编写测试代码的时候,发现存量的代码清一色使用的是mockito.MockUp()实现的,这种在一些不需要关注内部实现的场景下这个强行实现这就比较鸡肋,相比之下Mockito的用法更加的高级,本文就对Mockito的使用进行说明

一、为什么要使用Mockito

在涉及到需要mock的对象进行一些高级判定的时候,使用Mockito更加方便;
在只需要对mock的对象的一些函数进行是否执行的判定且不关系具体实现的函数,使用Mockito更加高效;
符合java8的流式写法,可读性更好,Mockito更加简洁。

二、如何使用mock

首先为什么要mock类的构造器呢?由于有些业务场景下类的构造器比较复杂没那么好构造时,就非常需要有必要将构造器mock掉。

2.1场景

我们需要简单的对一个非正式存在的对象调用API时,我们就使用mock

2.2 如何使用

创建一个无需实现方法输入输出的mock对象
方法如下:

Jedis jedis = Mockito.mock(Jedis.class);
//doTest
Mockito.verify(jedis, Mockito.times(1)).set("uniep.res","hjk1");

实例如下:

Jedis jedis = Mockito.mock(Jedis.class);
setStatic(MicroSvcPublish.class.getDeclareFiled("client", jedis));
microSvcPublish.publishCurrentMicroSvcs();//测试的方法
//验证publishCurrentMicroSvcs是否正常地向redis中写入数据
Mockito.verify(jedis, Mockito.times(1)).set("uniep.res","hjk1");

这个时候有人可能要说了,如果不是验证set这种不需要关注放回值的代码,那返回的默认值可能会导致直接要测试的方法执行报错了,请往下看。

2.3 实现mock对象的特定方法的行为

实现对象的方法行为

  • doNothing//默认创建出来的对象就是doNothing
  • doReturn//放回指定的对象·doThrow//抛出指定的异常
  • doCallRealMethod//调用真正的函数内容
    实例如下:
Jedis jedis=Mockito.mock(Jedis.class);
setStatic(MicroSvcPublish.class.getDeclaredField("cl
Mockito.doReturn("hjk1").when(jedis).get("hjk1");
Mockito.doNothing().when(jedis).get("hjk2");
microSvcPublish.publishCurrentMicroSvcs();//测试的方法
//验证publishCurrentMicroSvcs是否正常地向redis中写入想要的数据
Mockito.verify(jedis,Mockito.times(1)).set("uniep.res","hjk1");

2.4使用注解

2.4.1 常见的注解

  • @lnjectMocks
  • @Spy
  • @Mock
    直接上用法吧,使用@lnjectMocks注解可以实现Mock/Spy对象的自动注入。例如使用@lnjectMocks注解指定C类的实例,再使用@Mock/@Spy注解指定C类中包含的成员变量A、B类的实例,可以实现将A、B类的Mock/Spy 对象自动注入至C类实例中。
@lnjectMocks
private C c;
@Spy
private Bb=new B();
@Mock
private A a;

2.4.2 使用注解的配置

在使用Mockito的注解(比如说@Mock,
@Spy)等时,我们需要做如下配置:
·调用MockitoAnnotations.initMocks(this)方法对注解对象进行初始化
·使用内置的runner:@RunWith(MockitoJUnitRunner.class)
只有Annotation还不够,要让它们工作起来还需要进行初始化工作。
初始化的方法为MockitoAnnotations.initMocks(testClass)参数,testClass是你所写的测试类。一般情况下在Junit4的@Before定义的方法中执行初始化工作,如下Java代码:

@Before
public void initMocks(){
    MockitoAnnotations.initMocks(this);
}

除了上述的初始化的方法外,还可以使用Mockito 提供的 Junit Runner:MockitoJUnitRunner这样就省略了上面的步骤。

@RunWith(MockitoJUnit44Runner.class)
public class ExampleTest{
    ...
}

2.4.3使用注解的弊端

不过不推荐使用@lnjectMocks注解实现Mock/Spy对象的自动注入,当需要使用与@lnjectMocks注解类似的功能时,可以使用Whitebox.setInternalState()等方法通过反射的方式对成员变量进行替换。
不推荐的原因如下:

  • 使用不方便,对于@lnjectMocks注解指定的类的实例,需要使用@Mock/@Spy注解指定每个成员变量,未指定的成员变量值会为null;
  • 执行成员变量的真实方法不方便;
  • 对于@lnjectMocks注解指定的类的实例,注入的成员变量为Mock/Spy对象,当需要执行成员量的真实方法时不方便。

三、mock还是spy?

3.1场景

首先我们通过一个例子来学习下如何使用spy,我们只需要简单的对一个真实的对象调用API,Mokito.spy().我们便可以在把持该对象的所有正常方法调用的同时,还能像mock对象一样来对他配置一些行为。

3.2如何使用

@Test
public void whenSpyingOnList_thenCorrect_spy(){
    List<string>list = new ArrayList<string>();
    List<string>spyList=Mockito.spy(list);
    spyList.add("one");
    spyList.add("two");
    Mockito.verify(spyList).add("one");
    Mockito.verify(spyList).add("two");
    assertEquals(2, spyList.size());
}

@Test
public void whenSpyingOnList_thenCorrect_mock(){
    List<string>list =new ArrayList<string>();
    List<string> spyList = Mockito.mock(list);
    spyList.add("one");
    spyList.add("two");
    Mockito.verify(spyList).add("one");
    Mockito.verify(spyList).add("two");
    assertEquals(0, spyList.size());
}

请注意,list对象的真实的add方法被调用,同时spyList的size变为了2

3.3@Spy注解

接着,我们看一下如何使用@Spy注解,下面的例子中我们使用@Spy注解替换了spy()方法:

@Spy
List<string> spyList = new ArrayList<string>();

@Test
public void whenUsingTheSpyAnnotation_thenObj(){
    spyList.add("one");
    spyList.add("two");
    Mockito.verify(spyList).add("one");
    Mockito.verify(spyList).add("two");
    assertEquals(2,spyList.size());
}

四 FAQ

4.1 org.mockito.Mockito跟mockit.MockUp能够共用

不行

new MockUp<Jedis>(){
    //无效的 ↓↓
    return "hjk";
}
Jedis jedis = Mocito.mock(Jedis.class);
//有效的 ↓↓
Mockito.doReturn("hjk1").when(jedis).get(Mockito.anyString);
microSvcPublish.publishCurrentMicroSvcs();//测试的方法
jedis.verify(jedism Mockito.times(1)).set("key", "hjk1");

参考链接:
https://youngfor.me/2017/07/30/mockito-spy/
https://blog.****.net/u012129558/article/details/79106051

(以上内容为DreamKite本人原创,转载请附上原文链接)

上一篇:js实现call,apply和bind


下一篇:谈谈Spring的ConfigurationClassPostProcessor