Mockito-测试框架的运用

Mockito-测试框架的运用

Mockito是什么?

1、Mockito是一个简单的流行的Mock框架。它允许你创建和配置mock对象。使用Mockito可以明显的简化对外部依赖的测试类的开发。

2、Mock测试就是在测试过程中,对某些不容易构造或者不容易获取的对象,用一个虚拟的Mock对象来创建以便测试的测试方法。

3、Mock最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

Mockito用来做什么?

Mockito可用来帮助我们完成系统的单元测试、集成测试,提高我们编码的质量和可靠性。

Mockito的使用步骤

一般使用 Mockito 需要执行下面几个步骤:

  • 初始化Mockito的注解。
  • 模拟并替换测试代码中外部依赖;
  • 执行测试代码;
  • 验证测试代码是否被正确的执行。

Mockito注解初始化

要让Mockito的注解工作起来还需要进行初始化工作。

初始化的方法为:MockitoAnnotations.initMocks(testClass)参数testClass是你所写的测试类。一般情况下在Junit4的@Before定义的方法中执行初始化工作,如下:

Java代码

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

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

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

Mockito主要注解

1、@Spy

@Spy注解用于监视真实的对象实例,它生成的对象实例不受Spring管理。

@Spy注解生成的对象实例默认会调用真实的方法,执行方法内部的逻辑,有返回值的返回真实的返回值。

2、@SpyBean

@SpyBean注解生成的对象实例受Spring管理,相当于自动替换对应类型bean的注入,比如@Autowired等注入。

但与@Spy不同的是,它不会生成一个Bean的替代品装配到类中,而是会监听一个真正的 Bean 中某些特定的方法,并在调用这些方法时给出指定的反馈。却不会影响这个 Bean 其它的功能。

@SpyBean 包裹着真正的Bean装配到了依赖它的业务对象中,并对特定的行为作出反应。

3、@Mock

@Mock主要用于预先设定对象实例的行为逻辑,即模拟方法的返回值等等。

使用@Mock注解来定义mock对象有如下的优点:

  1. 方便mock对象的创建。
  2. 减少mock对象创建的重复代码。
  3. 提高测试代码可读性。
  4. 变量名字作为mock对象的标示,所以易于排错。

4、@MockBean

@MockBean注解生成的对象实例受Spring管理,相当于自动替换对应类型bean的注入,比如@Autowired等注入。

@MockBean包裹着模拟的Bean装配到了依赖它的业务对象中,并对特定的行为作出反应。

5、@InjectMocks注解

通过这个注解,可实现自动注入mock对象。

当前版本只支持setter的方式进行注入,Mockito首先尝试类型注入,如果有多个类型相同的mock对象,那么它会根据名称进行注入。

当注入失败的时候Mockito不会抛出任何异常,所以你可能需要手动去验证它的安全性。

Mockito注解使用示例

银行报文组装类代码

/**
 * 工行-查询余额-组装指令类
 *
 * @author chenlw
 * @since 2020/03/20
 */
@Component
public class IcbcQueryBalanceAssembler {

    /**
     * 组装查询余额报文
     *
     * @param accountInfo 账户信息
     * @return 查询余额报文
     */
    public Object getQueryBalanceOutput(Object accountInfo) {
        System.out.println("=======组装账户余额查询报文 start=====");
        System.out.println("执行组装账户余额查询报文操作1");
        System.out.println("执行组装账户余额查询报文操作2");
        System.out.println("执行组装账户余额查询报文操作3");
        System.out.println("·····");
        System.out.println("=======组装账户余额查询报文 end=====");
        System.out.println();
        return "账户余额查询报文";
    }

}

@Spy注解使用示例

测试代码:

public class IcbcQueryBalanceAssemblerSpyTest {

    @Spy
    private IcbcQueryBalanceAssembler icbcQueryBalanceAssemblerSpy;

    @Before
    public void init() throws Exception {
        // 初始化Mock类
        MockitoAnnotations.initMocks(this);
    }

    /**
     * Spy注解生成的实例对象不受Spring控制,实例对象默认会调用真实的方法,有返回值的返回真实的返回值。
     */
    @Test
    public void testSpy() {
        System.out.println("====测试@Spy注解=====");
        // 实例对象默认会调用真实的方法,有返回值的返回真实的返回值。
        System.out.println(icbcQueryBalanceAssemblerSpy.getQueryBalanceOutput("test"));
        System.out.println();
    }

}

输出结果:

====测试@Spy注解=====
=======组装账户余额查询报文 start=====
执行组装账户余额查询报文操作1
执行组装账户余额查询报文操作2
执行组装账户余额查询报文操作3
·····
=======组装账户余额查询报文 end=====

账户余额查询报文

输出结果为对象实例的真实行为。

@SpyBean注解使用示例

示例代码:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringBootMockitoApplicationTest.class)
public class IcbcQueryBalanceAssemblerSpyBeanTest {

    @SpyBean
    private IcbcQueryBalanceAssembler icbcQueryBalanceAssemblerSpyBean;


    @Before
    public void init() throws Exception {
        // 初始化Mock类
        MockitoAnnotations.initMocks(this);
    }


    /**
     * SpyBean注解生成的对象实例受Spring管理,相当于自动替换对应类型bean的注入,比如@Autowired等注入。
     */
    @Test
    public void testSpyBean() {
        System.out.println("====测试@SpyBean注解=====");
        // 实例对象默认会调用真实的方法,有返回值的返回真实的返回值。
        System.out.println(icbcQueryBalanceAssemblerSpyBean.getQueryBalanceOutput("test"));
        System.out.println();
    }
    
    @Test
    public void testSpyBean1() {
        System.out.println("====测试@SpyBean注解=====");
        Mockito.when(icbcQueryBalanceAssemblerSpyBean.getQueryBalanceOutput(Mockito.any())).thenReturn("===当调用该方法时,返回此模拟数据===");
        // 对特定的行为作出反应。
        System.out.println(icbcQueryBalanceAssemblerSpyBean.getQueryBalanceOutput("test"));
        System.out.println();
    }

}

测试真实行为代码:

@Test
public void testSpyBean() {
    System.out.println("====测试@SpyBean注解=====");
    // 实例对象默认会调用真实的方法,有返回值的返回真实的返回值。
    System.out.println(icbcQueryBalanceAssemblerSpyBean.getQueryBalanceOutput("test"));
    System.out.println();
}

输出结果:

====测试@SpyBean注解=====
=======组装账户余额查询报文 start=====
执行组装账户余额查询报文操作1
执行组装账户余额查询报文操作2
执行组装账户余额查询报文操作3
·····
=======组装账户余额查询报文 end=====

账户余额查询报文

输出结果为对象实例的真实行为。

对特定的行为作出反应的测试代码:

@Test
public void testSpyBean1() {
    System.out.println("====测试@SpyBean注解=====");
    Mockito.when(icbcQueryBalanceAssemblerSpyBean.getQueryBalanceOutput(Mockito.any())).thenReturn("===当调用该方法时,返回此模拟数据===");
    // 对特定的行为作出反应。
    System.out.println(icbcQueryBalanceAssemblerSpyBean.getQueryBalanceOutput("test"));
    System.out.println();
}

输出结果:

====测试@SpyBean注解=====
=======组装账户余额查询报文 start=====
执行组装账户余额查询报文操作1
执行组装账户余额查询报文操作2
执行组装账户余额查询报文操作3
·····
=======组装账户余额查询报文 end=====

===当调用该方法时,返回此模拟数据===

方法返回值返回的是预先设定的值,并非真实返回值。

@Mock注解使用示例

示例代码:

public class IcbcQueryBalanceAssemblerMockTest {

    @Mock
    private IcbcQueryBalanceAssembler icbcQueryBalanceAssemblerMock;

    @Before
    public void init() throws Exception {
        // 初始化Mock类
        MockitoAnnotations.initMocks(this);
    }

    /**
     * Mock注解主要用于预先设定对象实例的行为逻辑,即模拟方法的返回值等等。
     */
    @Test
    public void testMock() {
        System.out.println("=====测试@Mock注解=====");
        Mockito.when(icbcQueryBalanceAssemblerMock.getQueryBalanceOutput(Mockito.any())).thenReturn("===当调用该方法时,返回此模拟数据===");
        // 执行mock预先设定的返回信息,不会执行它原本真实的代码,所以没有打印该方法内部的任何信息
        System.out.println(icbcQueryBalanceAssemblerMock.getQueryBalanceOutput("test"));
        System.out.println();
    }

}

对特定的行为作出反应的测试代码:

@Test
public void testMock() {
    System.out.println("=====测试@Mock注解=====");
    Mockito.when(icbcQueryBalanceAssemblerMock.getQueryBalanceOutput(Mockito.any())).thenReturn("===当调用该方法时,返回此模拟数据===");
    // 执行mock预先设定的返回信息,不会执行它原本真实的代码,所以没有打印该方法内部的任何信息
    System.out.println(icbcQueryBalanceAssemblerMock.getQueryBalanceOutput("test"));
    System.out.println();
}

输出结果:

=====测试@Mock注解=====
===当调用该方法时,返回此模拟数据===

执行mock预先设定的返回信息,不会执行它原本真实的代码,所以没有打印该方法内部的任何信息

@MockBean注解使用示例

示例代码

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringBootMockitoApplicationTest.class)
public class IcbcQueryBalanceAssemblerMockBeanTest {

    @MockBean
    private IcbcQueryBalanceAssembler icbcQueryBalanceAssemblerMockBean;

    @Before
    public void init() throws Exception {
        // 初始化Mock类
        MockitoAnnotations.initMocks(this);
    }

    /**
     * MockBean注解生成的对象实例受Spring管理,相当于自动替换对应类型bean的注入,比如@Autowired等注入。
     */
    @Test
    public void testMockBean() {
        System.out.println("=====测试@MockBean注解=====");
        Mockito.when(icbcQueryBalanceAssemblerMockBean.getQueryBalanceOutput(Mockito.any())).thenReturn("===当调用该方法时,返回此模拟数据===");
        // 执行mock预先设定的返回信息,不会执行它原本真实的代码,所以没有打印该方法内部的任何信息
        System.out.println(icbcQueryBalanceAssemblerMockBean.getQueryBalanceOutput("test"));
        System.out.println();
    }

}

输出结果:

=====测试@MockBean注解=====
===当调用该方法时,返回此模拟数据===

执行mock预先设定的返回信息,不会执行它原本真实的代码,所以没有打印该方法内部的任何信息


源代码地址:https://github.com/chenliwu/my-spring-boot-example/tree/master/spring-boot-mockito

上一篇:原码、反码、补码


下一篇:ASCII码表含义