参数化测试与Mock

参数化测试与Mock

转载自https://blog.csdn.net/sunliduan/article/details/42026509java

单元测试概念

说到测试,你们都不会陌生,从咱们开始学习编程开始,就知道测试。测试和编程就像两个双胞胎似的,但是,显然咱们更钟情于双胞胎中的一个--编程。一些人可能对测试了然于胸,却匮乏于行动,一些人也可能对测试只是闻其名不知其意。下面这篇博文就是给你们在零基础上讲解一下Java中单元测试的使用。程序员

首先来讲说,究竟什么是单元测试?单元测试是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,通常来讲,要根据实际状况去断定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中能够指一个窗口或一个菜单等。能够说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程当中要进行的最低级别的测试活动,软件的独立单元将在与程序的其余部分相隔离的状况下进行测试。总的来讲,可总结成如下四点:web

1. 人为规定编程

2. 最小被测功能模块数组

3. 最低级别maven

4. 不依赖其余单元函数

知道什么是单元测试了,咱们立刻就要想到,单元测试有什么用呢?我为何要进行单元测试呢?下面从如下四点来讲明单元测试的好处:工具

  1. 提升代码质量

稍有信息素质的专业程序员老是追求着一件事情---写出优雅的代码。这里的优雅,不只仅是指需求功能的准确实现,更是系统上线后的稳定和高性能。而测试用例的认真思考与书写,就给了程序员一个“深思熟虑”的机会,让咱们在“作”以前先“想”好了。固然,这可能须要丰富的编程经验。不过我也相信,经验是一点点积累来的,因此从如今开始,为时不晚。性能

  1. 减小调试时间

咱们之前的测试,基本上都是从web层开始,一条线的测试。首先这种测试须要咱们打包部署后运行整个程序来执行,耗费时间较多;其次也是最重要的,出现错误后咱们不能很快的定位是那一层的问题,只有一步一步的断点调试,方可定位到错误,这样调试的时间是很长的。单元测试

而在Java中的单元测试,通常是对一个类的测试。而这个偏偏让coder极为迅速而且准确的定位错误的来源---就是本类!所以,极大的减小了咱们调试的时间。

  1. 隔离测试

在一个大项目或者关系比较紧密的项目中,颇有可能出现两个子系统之间的接口依赖,例如此次高校云平台的项目,其余子系统都须要基础系统为其提供接口,所以很可能会形成这种状况,前期开发中基础系统一直在开发接口,而本身的功能只能放后!

怎么才能解决这个问题呢?隔离测试!它使得咱们能够测试还未写完的代码(只要你又接口可以使用),另外,隔离测试能帮助团队单元测试代码的一部分,而无需等待所有代码的完成

测试方法

知道什么是单元测试,而且单元测试有什么用了,下面咱们就举几个很简单的例子来讲明一下单元测试该怎么用!网上一查,单元测试的工具类有不少,再此我选用最流行的JunitMockito进行测试演示。、

先来讲说准备条件吧:

首先是引入相关Jar包--Junit4.11和Mockito-groovy-support1.2。本次高校平台是使用的Maven进行jar包管理,所以我只须要在项目的pom.xml文件下写入该jar包便可;若是读者你的项目没有使用Maven,那么就须要手动引入相关jar了。

其次就是创建测试类了,直接右击实际类--new--Junit test case便可。这里须要使用Maven的朋友注意,测试类要求放在Mavenproject自动生成的src/test/java文件夹下,这样每次运行maven的时候都会自动运行该文件下的测试类,而且这些类在打包的时候不会打入,仅为了测试存在。

有了条件了,下面咱们就要开始填充一下新建的测试类了。

(一)普通测试

待测方法:

//加法
publicint add(inta, int b) {
    returna + b;
}

测试方法:

@Test
publicvoidtestAdd(){
    //--------------------第一种写法----------------------
    //(1)待测方法的“参数赋值”
    int a =1;
    int b=3;

    //(2)赋值后的“指望值”
    intexpectedReturn=6;

    //(3)调用待测方法,获得“实际值”
    intactualReturn = firstDemo.add(1, 3);

    //(4)经过断言,判断“指望值”和“实际值”是否相等
    assertEquals(expectedReturn,actualReturn);

    //---------------------第二种写法------------------------
    //assertEquals(4,firstDemo.add(1,3));                
}

其实一个测试方法的书写就是四个步骤:

(1)参数赋值

(2)写出指望

(3)获取实际值

(4)断言--比较指望值和实际值。

因此其实一个测试方法的填充是很简单的,咱们只须要把这几个步骤都写出来就好啦。固然,在实际项目中,我上述方法太简单,通常状况下是不用进行测试的。咱们只须要对一些业务逻辑复杂的方法进行测试便可。这里举这个例子,只是为了让零基础的读者可以很快而且容易的理解单元测试怎么写。

(二)参数化测试

上面第一个普通测试,是针对一个方法只须要一个测试用例便可完成测试的状况。在实际项目中,咱们会遇到一些分支语句,这时候一个测试用例已经不能知足咱们覆盖所有分支语句了。因此咱们就须要写多个测试用例,但是咱们必须针对这个待测方法,写多个测试方法吗?这也太麻烦了吧!没有什么解决办法吗?若是是Junit3的话,咱们只能这样作,但是从Junit4开始,加入了一个新的概念--参数化测试。这就为咱们解决了这个问题。

参数化测试主要包括五个步骤:

(1)为准备使用参数化测试的测试类指定特殊的运行器org.junit.runners.Parameterized。

(2)为测试类声明几个变量,分别用于存放指望值和测试所用数据。

(3)为测试类声明一个带有参数的公共构造函数,并在其中为第二个环节中声明的几个变量赋值。

(4)为测试类声明一个使用注解org.junit.runners.Parameterized.Parameters修饰的,返回值为 java.util.Collection的公共静态方法,并在此方法中初始化全部须要测试的参数对。

(5)编写测试方法,使用定义的变量做为参数进行测试。

待测方法:

//多分支语句
public  boolean Parameterization(int a){
    if(a>10) {
     returntrue;
    }else{
     returnfalse;
    }
}

测试方法:

@RunWith(Parameterized.class)//第一步:指定特殊的运行器org.junit.runners.Parameterized
publicclassFirstDemoTestParameterization {
    //要测试的类
    private FirstDemo firstDemo;        
    
    //第二步:为测试类声明几个变量,分别用于存放指望值和测试所用数据。
    privateint input1; 
    private boolean expected;

    @Before //执行每一个测试方法以前都执行一次
    publicvoid setUp() throws Exception {
        firstDemo=newFirstDemo();
    }

    //第三步:带有参数的公共构造函数,并在其中为声明的几个变量赋值。
    public FirstDemoTestParameterization(intinput1,boolean expected) {
        this.input1= input1;  //参数1
        this.expected= expected;  //期待的结果值
    }
    
   //-------------------(1)参数赋值 &&&(2)写出指望值----------------------------        

   //第四步:为测试类声明一个注解@Parameters,返回值为Collection的公共静态方法,并初始化全部须要测试的参数对。
   @Parameters
   publicstaticCollection prepareData() {
       Object[][]object = { { -1,true }, { 13, true } };    //测试数据
       returnArrays.asList(object); //将数组转换成集合返回
    }
    
    @Test
    publicvoidtestParameterization() {
        //-----------(3)获取实际值&&&(4)断言--比较指望值和实际值。---------------        
        //第五步:编写测试方法,使用定义的变量做为参数进行测试。
        assertEquals(expected,firstDemo.Parameterization(input1));
    }
}

关于参数化测试,咱们须要注意如下几点:

(1)@RunWith(Parameterized.class):在每一个须要参数化测试的类上面,咱们都须要写上@RunWith(Parameterized.class)由于这样JUnit才会使用Parameterized运行器来运行测试,不然JUnit会选择默认的运行器,而默认运行器的不支持参数化测试。

(2)@Parameters:在提供数据的方法上加上一个@Parameters注解,这个方法必须是静态static的,而且返回一个集合Collection。

(三)隔离测试

隔离测试也是咱们经常使用的一个防止多类之间依赖的测试。最基础的就是B层对D层的依赖。测试B层时,咱们不可能还要跑D层,这样的话就不是单元测试。那么咱们怎么来解决这个问题呢?咱们不须要跑D层,可是又须要D层的返回值。隔离测试就帮助咱们解决了这个问题。在本次项目中,我选用Mockito来进行隔离测试。

其实说白了,隔离测试,就是一个Mock--模拟的功能。当咱们依赖其余类时,不须要真实调用,只需模拟出该类便可。具体使用以在下面demo中给出了详细的解释。

直接上B层的代码,待测方法:

publicbooleanIsExist(Student student) {

    ListstudentList = null;
    booleanisExist =true;

    //若输入学生姓名不为空
    if(student.getName() != null) {
        //调用Dao层的查询学生方法,并将信息返回List中
        studentList= studentDao.queryStudent(student.getName());
        //判断Dao层返回List是否为空,若是为空及学生不存在,则返回false,不然返回true
        if(studentList.isEmpty()) {
            isExist=false;
        }else{
            isExist=true;
        }
    }
    returnisExist;
}

测试方法:

packagecom.sld.service;

import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
import com.sld.dao.StudentDao;
import com.sld.entity.Student;

@RunWith(MockitoJUnitRunner.class)
publicclassStudentServiceTest {

    @Mock  //建立Mock对象,模拟studentDao类
    StudentDaostudentDao;

    @InjectMocks //自动注入Mock类(StudentDao)到被测试类(StudentService),做为一个属性
    StudentServicestudentService;

    @Test
    public void testIsExist(){        

        //------------------(一)指望值:隔离Dao层后,本身设定的指望Dao层的返回值-----------

        //(1)实例化一个实体类expectStudent,并为其赋值
        Student expectStudent = new Student();
        expectStudent.setName("001");

        //(2)实例化一个List集合,并将赋值后的expectStudent实体类放入集合中 
        List<Student> mockStudentList =newArrayList<Student>();
        mockStudentList.add(expectStudent);

        //(3)当调用模拟类的方法时,返回List集合
        when(studentDao.queryStudent(expectStudent.getName())).thenReturn(mockStudentList);

        //------------------(二)实际值:测试Service层方法,而且返回实际值-----------
        Student actualStudent1 = new Student();
        actualStudent1.setName("001");

        //学生信息:存在,应该返回true
        booleanres= studentService.IsExist(actualStudent1);
        System.out.print(res);

        //------------------(三)指望值与实际值比较:测试Service层方法,而且返回实际值-----------
        assertTrue(studentService.IsExist(actualStudent1));

        //------------------补充:为了对比写的-----------
        Student actualStudent2 = new Student();
        actualStudent2.setName("002");

        //学生信息:不存在,应该返回false
        booleanres2= studentService.IsExist(actualStudent2);
        System.out.print(res2);
        assertTrue(studentService.IsExist(actualStudent2));     
        //assertTrue(studentService.IsExist(actualStudent2));
    }
}

经过一步步的讲解,不知道你对单元测试了解多少了呢?不论是普通测试,仍是参数化测试,仍是隔离测试,咱们新接触时确定不可能写的特别完善。写测试和写代码是同样的,都须要咱们一点一滴经验的积累。因此不要以为测试类难或者麻烦,慢慢来,习惯都是培养出来的。一句很老套的话:学习--就是一个过程~~

相关文章
相关标签/搜索