单元测试--Junit和Mockito

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

---------------------------什 么 是-----------------------------

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

 1. 人为规定

   2. 最小被测功能模块

   3. 最低级别

   4. 不依赖其他单元

--------------------------什 么 用-----------------------------

知道什么是单元测试了,我们马上就要想到,单元测试有什么用呢?我为什么要进行单元测试呢?下面从以下四点来说明单元测试的好处:

1. 提高代码质量

        ----实现功能

        ----逻辑严密

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

2. 减少调试时间

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

而在Java中的单元测试,一般是对一个类的测试。而这个恰恰让coder极为迅速并且准确的定位错误的来源---就是本类!因此,极大的减少了我们调试的时间。

3. 隔离测试

在一个大项目或者关系比较紧密的项目中,很有可能出现两个子系统之间的接口依赖,例如这次高校云平台的项目,其他子系统都需要基础系统为其提供接口,因此极可能会造成这种情况,前期开发中基础系统一直在开发接口,而自己的功能只能放后!

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

---------------------------怎 么 用----------------------------

知道什么是单元测试,并且单元测试有什么用了,下面我们就举几个很简单的例子来说明一下单元测试该怎么用!网上一查,单元测试的工具类有很多,再此我选用最流行的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)待测方法的“参数赋值”
inta =1;
intb=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;

importstaticorg.junit.Assert.*;

importjava.util.ArrayList;
importjava.util.Collection;
importjava.util.List;

importorg.junit.Before;
importorg.junit.Test;
importorg.junit.runner.RunWith;
importorg.mockito.InjectMocks;
importorg.mockito.Mock;
importorg.mockito.runners.MockitoJUnitRunner;

importstaticorg.mockito.Mockito.when;

importcom.sld.dao.StudentDao;
importcom.sld.entity.Student;

@RunWith(MockitoJUnitRunner.class)
publicclassStudentServiceTest {

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

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

@Test
publicvoid 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层方法,并且返回实际值-----------
StudentactualStudent1 = new Student();
actualStudent1.setName("001");
//学生信息:存在,应该返回true
booleanres= studentService.IsExist(actualStudent1);
System.out.print(res);

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

//------------------补充:为了对比写的-----------
StudentactualStudent2 = new Student();
actualStudent2.setName("002");
//学生信息:不存在,应该返回false
booleanres2= studentService.IsExist(actualStudent2);
System.out.print(res2);
assertTrue(studentService.IsExist(actualStudent2));
//assertTrue(studentService.IsExist(actualStudent2));
}

}

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

时间: 2024-07-29 17:45:43

单元测试--Junit和Mockito的相关文章

Java单元测试(Junit+Mock+代码覆盖率)---------转

Java单元测试(Junit+Mock+代码覆盖率) 原文见此处 单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复.改进或重构之后的正确性. 一般来说,单元测试任务包括 接口功能测试:用来保证接口功能的正确性. 局部数据结构测试(不常用):用来保证接口中的数据结构是正确的 比如变量有无初始值 变量是否溢出 边界条件测试 变量没有赋值(即为NULL) 变

单元测试Junit

###<center> 单元测试Junit </center>###- - -1.**单元测试**:> ==单元测试==是软件之中对于最小的功能模块的的测试,其可以对最基本的软件构成单元来测试.> 需要注意的是:> >**测试用例是用来达到测试想要的预期结果,而不能测试出程序的逻辑错误**. 2.**JUnit**:>1.**Junit是基于断言机制的**.是用于编写可复用测试集的简单框架,是xUnit的一个子集.xUnit是一套基于测试驱动开发的测试

Android Day02-Android中单元测试(junit测试)&monkey测试

Android中junit测试有2种实现方式 第1种:一般Android工程的实现方式 1.在清单文件中添加2项内容 首先在AndroidManifest.xml中加入下面红色代码: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.itcast.action" android:versionCode="1"  android:v

Java 单元测试Junit

@Test @Before @After 测试方法运行前执行Before动作(比如创建资源),运行后执行After动作(比如销毁资源) @BeforeClass @AfterClass 测试类运行前执行Before动作(比如创建资源),运行后执行After动作(比如销毁资源) Assert 对方法结果进行判断,是否等于希望值,是否为空等 表示测试通过不通过 Java 单元测试Junit,布布扣,bubuko.com

菜鸟学Java(二十一)——如何更好的进行单元测试——JUnit

测试在软件声明周期中的重要性,不用我多说想必大家也都非常清楚.软件测试有很多分类,从测试的方法上可分为:黑盒测试.白盒测试.静态测试.动态测试等:从软件开发的过程分为:单元测试.集成测试.确认测试.验收.回归等. 在众多的分类中,与开发人员关系最紧密的莫过于单元测试了.像其他种类的测试基本上都是由专门的测试人员来完成,只有单元测试是完全由开发人员来完成的.那么今天我们就来说说什么是单元测试,为什么要进行单元测试,以及如更好的何进行单元测试. 什么是单元测试? 单元测试(unit testing)

上课笔记_单元测试JUnit

单元测试  单元测试就是整合程序代码到系统的其余部分之前先测试以便找出程序代码中的bugs,单元测试是白盒测试: 有很多测试工具我们为什么要选择JUnit测试工具? 1. 开源 2. 使用简单 3. 可以扩展功能 MyEclipse集合了JUnit:这里通过导入jar包来实现. Junit4下载地址:http://pan.baidu.com/s/1kTLZa3X 我们一般在项目右击新建一个source foler :test  :(发布的时候test不会被发布) 新建包名和要测试的类的包名一样:

Java单元测试Junit的Annotation介绍

单元测试是开发者的业界良心 Java开发中常以Junit作为测试框架,而且Junit也成为Java社区测试方面的事实标准,可以百度一些看看Junit的相关介绍.目前Junit发展到了Junit4.x版本,相对之前的Junit3.x版本有着明显的变化就是使用了Annotation的方式来注解测试用例. 下面是关于Junit4.x的常用注解说明图: Java单元测试Junit的Annotation介绍

Java单元测试(Junit+Mock+代码覆盖率)

原文见此处 单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复.改进或重构之后的正确性. 一般来说,单元测试任务包括 接口功能测试:用来保证接口功能的正确性. 局部数据结构测试(不常用):用来保证接口中的数据结构是正确的 比如变量有无初始值 变量是否溢出 边界条件测试 变量没有赋值(即为NULL) 变量是数值(或字符) 主要边界:最小值,最大值,无穷大(

单元测试(Junit+Mock+代码覆盖率)

单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复.改进或重构之后的正确性. 一般来说,单元测试任务包括 接口功能测试:用来保证接口功能的正确性. 局部数据结构测试(不常用):用来保证接口中的数据结构是正确的 比如变量有无初始值 变量是否溢出 边界条件测试 比如对于Math.sqrt,给出n^2-1,和n^2+1的边界 空集合 对集合的大小应用"数值变量