思沃大讲堂2018软件测试

「测试」Mock之Mockito认识之旅

2018-01-31  本文已影响83人  廖雪青
一.前言

  关于这篇文章的起源,是第三次思沃大讲堂的作业的题目中,有这样一段话

把前两问做的类集成起来,写一个集成的单元测试,写一个集成测试。

问题来了,集成的单元测试和集成测试有什么区别呢?
  集成测试(Integration Testing):是在单元测试的基础上,将所有模块按照概要设计要求组装成为一个子系统或者系统,进行集成测试。一些模块虽然能够单独工作,但并不能保证连接起来也能正常的工作,程序在某些局部反映不出来的问题,在全局上很可能暴漏出来,因此集成测试十分必要。
  集成的单元测试:按字面意思的理解,就是对该集成类进行单元测试。单元测试就是对已经实现的软件最小单元进行测试以保证构成软件系统的各个单元的质量。这么说来,在此处集成的单元测试和集成测试并无区别吗?
  No~ 区别还是有的,集成的单元测试,首先是个单元测试,然后是对集成类进行单元测试,也就是说只测试该类的逻辑,而不用关注他所依赖的类是否正确实现。如何不关注依赖类的是否正确实现呢?这就是接下来我要介绍的Mock啦,这得感谢我的Buddy,让我对Mock有了直白的认识,然后才关注Mock,从而进一步了解。


二.为什么需要mock

  我们在做测试的时候,往往会发现我们要测试的类或方法会引用很多外部依赖的对象,而我们没法控制这些外部依赖的对象,为了解决这个问题,我们需要用到Mock来模拟这些外部依赖的对象,从而控制它们。举个例子,service调用dao,即service依赖dao,我们可以用mock来模拟真实的dao调用,从而达到测试service的目的。
  模拟对象(Mock Object)可以取代真实对象的位置,用于测试一些与真实对象进行交互或依赖于真实对象的功能,模拟对象背后的目的就是创建一个轻量级的,可以控制的对象来代替测试中需要的真实对象,模拟真实对象的行为和功能。

mock对象使用范畴
1.真实对象具有不可确定的行为,产生不可预测的效果。
2.真实对象很难被创建的。
3.真实对象的某些行为很难被触发。
4.真实对象实际上还不存在的。


三.常见的mock框架

推荐Mockito和powermock


四.Mockito简单介绍

一般使用Mockito需要执行以下步骤:
1.模拟并替换测试代码中外部依赖。
2.执行测试代码。
3.验证测试代码是否被正确的执行


Mock执行步骤.png

使用注解(@Mock、@InjectMocks等)的话,必须实例化mock对象,有两种方式实例化mock对象:

当我们需要配置某个方法的返回值时,Mockito提供了链式的API供我们方便的调用:

需注意:


五.Mockito使用实例

以第三次思沃大讲堂的作业为例。
问题描述:游戏开始后,系统会随机给出一个四位,每位都不重复的数字作为答案。由用户输入自己猜测的 四个数字。 系统会将两个数字进行对比,并给形出xAxB的提示, 比如”2A1B”。 如果数字猜对而且位置也对,就是一个A。 如果数字猜对但位置不对,就是一个B。 例如:系统给出”1234”,用户输入”1234” 返回”4A0B” 系统给出”1234”,用户输入”4321” 返回”0A4B”。
CompareNumber类:实现比较。只有一个函数,该函数接受两个参数,一个是答案,一个是用户输 入的四位数。返回值是xAxB的字符串 。
AnswerGenerator类:生成随机的四位无重复位数字。只有一个函数,返回一个四位,每位都不重复随机数。
Guess类:只有一个函数,只有一个参数。把前两问做的类集成起来。
GuessUnitTest类:对Guess类写单元测试(即文首所说的集成的单元测试)。

public class Guess {
    private CompareNumber compareNumber = new CompareNumber();
    private AnswerGenerator answerGenerator = new AnswerGenerator();
    private int answer=answerGenerator.generatorFourDigits();

    public String guessTheDigit(int guessDigit){
        String result = compareNumber.compareToAnswer(answer,guessDigit);
        return result;
    }
}
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class GuessUnitTest {

    @InjectMocks
    private Guess guess = new Guess();

    @Mock
    private CompareNumber mockCompareNumber = new CompareNumber();

    @Mock
    private AnswerGenerator mockAnswerGenerator = new AnswerGenerator();

    @Before
    public void init() throws NoSuchFieldException, IllegalAccessException {
        /*返回guess已声明字段answer*/
        Field f = guess.getClass().getDeclaredField("answer");
        /*值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查*/
        f.setAccessible(true);
        /*将指f对象表示的字段answer设置为指定的值,即1234。*/
        f.set(guess, 1234);
    }

    @Test
    public void test_4A0B() {
        when(mockCompareNumber.compareToAnswer(1234, 1234)).thenReturn("4A0B");
        String result = guess.guessTheDigit(1234);
        assertTrue("4A0B".equals(result));
    }

    @Test
    public void test_0A4B() {
        when(mockCompareNumber.compareToAnswer(1234, 4321)).thenReturn("0A4B");
        String result = guess.guessTheDigit(4321);
        assertTrue("0A4B".equals(result));
    }

    @Test
    public void test_2A2B() {
        when(mockCompareNumber.compareToAnswer(1234, 1432)).thenReturn("2A2B");
        String result = guess.guessTheDigit(1432);
        assertTrue("2A2B".equals(result));
    }
}

说明
@Mock:创建一个mock对象(模拟对象)。
@InjectMock:创建一个实例,@Mock注解创建的模拟对象将被注入到该实例中。
@Before:Junit注解,在每个测试执行之前必须执行的代码。
@Test:Junit注解,标明是一个测试方法。

Junit4常用注解

在Junit4中,单元测试用例的执行顺序:


单元测试用例的执行顺序.png

每个测试方法的执行顺序:


测试方法的执行顺序.png
六.Mockito学习资料

Mockito官网:http://site.mockito.org/
Mockito官方文档:https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Mockito.html
Mockito中文文档:http://blog.csdn.net/bboyfeiyu/article/details/52127551
Mockito Github:https://github.com/mockito/mockito

上一篇 下一篇

猜你喜欢

热点阅读