本篇将会主要对单元测试的主流框架 JUnit 和 TestNG做由浅入深的介绍
单元测试概述
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
比如对函数abs(),我们可以编写出以下几个测试用例:
a.输入正数,比如1、1.2、0.99,期待返回值与输入相同;
b. 输入负数,比如-1、-1.2、-0.99,期待返回值与输入相反;
c.输入0,期待返回0;
d. 输入非数值类型,比如None、[]、{},期待抛出TypeError。
如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。
把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。
单元测试通过后有什么意义呢?如果我们对abs()函数代码做了修改,只需要再跑一遍单元测试,如果通过,说明我们的修改不会对abs()函数原有的行为造成影响,如果测试不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。
这种以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,可以极大程度地保证该模块行为仍然是正确的。
图片.png
下面先简单用一个实例demo来先简单的介绍下快速创建单元测试
一、使用Junit测试框架
Junit 介绍:
Junit是一套框架(用于JAVA语言),由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework),即用于白盒测试。
Junit是一款专门用于java语言的代码的单元测试工具;是一套基于java的测试框架,目前有两个版本:Junit3,Junit4.Junit中最常用的两个测试方法是:
assertEquals(expect,actual);用于非数组型的数据的比较;
assertArrayEquals(expect,actual);用于数组型的数据的比较;
PS:这两个函数的选择是由被测单元的输出数据类型决定的。
1.Junit 3特征
a.测试类都是要继承TestCase类:
import junit.framework.TestCase
public class TestCalc extends TestCase
b.测试类中的方法的作用是由方法名决定的且测试用例的方法必须以小写的test开头;
如:
图片.png
2、Junit 4有很大优化,没有Junit3那样的约束条件,使用起来很方便,对于Calc测试如下:
图片.png
3.对于Java中单元测试来说,被测的方法的类型一般是或者可以转化为以下四种类型:
a.被测方法有返回值,且返回值是可预期的。(按照功能,特定的输入一定对应特定的输出);assertEquals(预期值,输入值);
//上述对于加法函数的测试即属于该类;
b.被测方法有返回值,但是返回值不是固定的,是随机的,但是返回值从数学角度来看,返回值必然是有其特征的;
图片.png
图片.png
c.被测方法没有返回值,此时都可以将之转化为输出类型(System.out.println());
例:Dog这样的类是没有返回类型的,通常我们可以将之转化为有输出语句的类型;
图片.png
图片.png
图片.png
d.被测方法本身会抛出异常,此时的测试重点已经是对异常信息的检测。
图片.png
图片.png
图片.png
图片.png
图片.png
5.feeder工具
添加feeder插件,新建data文件夹,右击新建文件,命名为isnumber.csv.(以上面isNumber为例)
图片.png
图片.png
在idea中进行junit4的测试方法的自动生成的步骤
在被测试的类的方法上使用快捷键ctrl+shift+t或者直接右击
图片.png
图片.png
后续会具体介绍这里的选项
图片.png
之后就会自动在测试下生成测试类
图片.png
如上可以非常顺利的创建junit测试是因为前期的配置都已完成;现在针对不可以顺利使用junit进行单元测试的情况做如下的一些说明:
一、环境配置
使用idea IDE 进行单元测试,首先需要安装JUnit 插件。
1、安装JUnit插件步骤
File-->settings-->Plguins-->Browse repositories-->输入JUnit-->选择JUnit Generator V2.0安装
2.使用JUnit插件
在需要进行单元测试的类中,使用快捷键alt+insert,选择JUnit test,选择JUnit4。
3.配置生成的test类位置
图片.png
点击JUnit4,可以编辑模板
package test.$entry.packageName; -----可以更改生成的测试类的包名
更改默认包含的测试函数
图片.png
二、Junit的使用以及重点说明
1、用JUnit4进行单元测试,idea 中自动import 所需包
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
图片.png
2、在 JUnit4 中,测试是由 @Test 注释来识别的,测试类使用Test做为类名的后缀(非必要),测试方法使用test作为方法名的前缀(非必要)。测试方法必须使用public void 进行修饰,不能带有任何参数。测试类的包应该与被测试类的包保持一致。
3、JUnit4允许我们使用 @Before和@After来注释多个方法(替代JUnit3的setUp() 和tearDown() 方法),这些方法在每个方法执行前和执行后都要运行一次,完成初始化字段或准备数据和清除工作。
JUnit 4 常用的几个注解(@Before @After @Test @Ignore @BeforeClass @AfterClass)
@Before:初始化方法,在任何一个测试执行之前必须执行的代码;
@After:释放资源,在任何测试执行之后需要进行的收尾工作。在每个测试方法执行之后执行一次,该annotation只能修饰public void 方法;
@Test:测试方法,表明这是一个测试方法。在Junit中将会自动被执行。该annotation只你呢个修饰public void 方法。
@Ignore:忽略的测试方法,标注的含义就是“某些方法尚未完成,暂不参与此次测试”;
@BeforeClass:针对所有测试,只执行一次,且必须为public static void;一般用于初始化必要的消耗较大的资源,例如数据库连接等
@AfterClass:针对所有测试,将会在所有测试方法执行结束后执行一次,且必须为public static void;
图片.png
结果分析:
Passed说明没有错误
Failure 一般是单元测试使用的断言方法判断失败引起,说明预期结果和程序运行结果不一致。
error 是有代码异常引起的,他产生于测试代码本身中的Bug。
有关单元测试的相关注解
a. @BeforeClass 和@AfterClass
JUnit4引入了一个JUnit3中没有的新特性——类范围的 setUp() 和tearDown() 方法。任何用 @BeforeClass 注释的方法都将在该类中的测试方法运行之前刚好运行一次,而任何用 @AfterClass 注释的方法都将在该类中的所有测试都运行之后刚好运行一次。
@BeforeClass所修饰的方法在所有方法加载前执行,而且他是静态的在类加载后就会执行该方法,在内存中只有一份实例,适合用来加载配置文件。
@AfterClass所修饰的方法在所有方法执行完毕之后执行,通常用来进行资源清理,例如关闭数据库连接。
所以一个Junit 4 的单元测试用例执行顺序为:@BeforeClass –> @Before –> @Test –> @After –> @AfterClass;每一个测试方法的调用顺序为:@Before –> @Test –> @After。
注意:
@BeforeClass和@AfterClass对于所有测试方法只运行一次,而@Before和@After是每个测试方法都运行一次。多个before和after的执行顺序是不定的。
@BeforeClass和@AfterClass必须声明为public static,所有标识为@AfterClass的方法都一定会被执行,即使在标识为@BeforeClass的方法抛出异常的的情况下也一样会。而@Before和@After必须声明为public 并且非static。所有标识为@After 的方法都一定会被执行,即使在标识为 @Before 或者 @Test 的方法抛出异常的的情况下也一样会。
b.@Ignore:忽略的测试方法,标注的含义就是“某些方法尚未完成,暂不参与此次测试”;这样的话测试结果就会提示你有几个测试被忽略,而不是失败。一旦你完成了相应函数,只需要把@Ignore标注删去,就可以进行正常的测试。
c.@Test(timeout=毫秒) 允许程序运行的时间。
d.@Test(excepted=XX.class) 在运行时忽略某个异常。
e.参数化配置 (@Parameters)
进行单元测试的时候,通常一个方法需要好几个case进行测试,Junit提供参数化便于我们对方法进行多种参数的组合测试
如果不使用参数化进行测试的话,那么我们的测试类会做的很臃肿
参数化的实现过程(重要)
1 在测试类上增加
@RunWith(Parameterized.class)并引入
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
2 写上构造类的函数及定义入参
@RunWith(Parameterized.class)public class LogicServiceTest {
LogicService logserv ;
int parm1 ;//定义入参
int parm2 ;//定义入参
int res;//定义入参
public LogicServiceTest(int parm1,int parm2,int res){//定义构造函数
this.parm1=parm1;
this.parm2=parm2;
this.res=res;
}
3 定义一个返回结果为collection类型的方法并写上@Parameters
注意 Arrays.asList()里面NEW的Object的要与定义的参数一一对应
图片.png
执行了4次
总结:使用了参数化后测试类的代码简化了不少,而且执行起来效果很不错
图片.png
使用junit做单元测试和用代码实现的方式对比
参考地址[http://www.cnblogs.com/Carolinee/p/5382350.html]
对比1、对加法函数进行测试
图片.png
1.实例化被测单元(方法):类名 实例名=new 类名([参数])
2.调用被测单元,对比预期值和输出值(实际值);
在没有junit测试工具的情况下,我们要进行如下的测试代码编写过程
图片.png
图片.png
图片.png
参数化设置:需要测试的仅仅是测试数据,代码结构是不变的,只需要更改测试数据。用法如下:
在单元测试类前注解:@RunWith(Parameterized.class)需要导入import org.junit.runner.RunWith;和import org.junit.runners.Parameterized;,然后构造需参数化的构造器。写个函数用于输入需要测试的参数和期待结果,并在该函数前面注解@Parameters
实例demo的测试类
package com.example.controller;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.List;
/**
-
Created by wuxiaodong on 2017/8/30.
/
public class CalculatorTest extends Calculator {
@RunWith(Parameterized.class)
public static class testAdd {
int expected = 0;
int input1 = 0;
int input2 = 0;
public testAdd(int expected, int input1, int input2) {
System.out.println("Constructor Method with Parameters!");
System.out.println();
this.expected = expected;
this.input1 = input1;
this.input2 = input2;
}
@Before
public void before1() throws Exception {
System.out.println("Start1:");}
@Before
public void before2() throws Exception {
System.out.println("Start2:");}
@After
public void after() throws Exception {
System.out.println("Over!");}
@Parameterized.Parameters
public static List<Object[]> input(){
System.out.println("Initialize Data!");
return Arrays.asList(new Object[][]{{4,3,1},{6,2,5}});}
@Test
public void testAdd() throws Exception {
Calculator test = new Calculator();
Assert.assertEquals(expected,test.add(input1,input2));
}
}
}
以上就是参数化的测试注释
上例中故意在第二组数据中写错结果,跑程序之后就会显示第一组测试通过,第二组失败,即上面写道的failed,表示执行结果与期待结果不等。
图片.png
/* * 参数化测试必须的构造函数 * @param expected 期望的测试结果,对应参数集中的第一个参数 * @param input1 测试数据,对应参数集中的第二个参数
@param input2 测试数据,对应参数集中的第三个参数 /
修改后这两个单元测试的用例就都通过了
图片.png
4、常用的断言介绍(参考博客
http://blog.csdn.NET/afeilxc/article/details/6218908)
4.1 、assertEquals([String message],Object target,Object result)
target与result不相等,中断测试方法,输出message
图片.png
上述例子中使用的断言方法是
Assert.assertEquals(expected,test.add(input1,input2));
assertNull 断言对象为null,若不满足,方法抛出带有相应信息的AssertionFailedError异常。
assertEquals(a, b) 测试a是否等于b(a和b是原始类型数值(primitive value)或者必须为实现比较而具有equal方法)
assertEquals
断言两个对象相等,若不满足,方法抛出带有相应信息的AssertionFailedError异常。
例如计算器加法功能的测试可以使用一下验证:
Assert.assertEquals(0,result);
4.2 assertTrue/False([String message],Boolean result)
Result为 false/true,中断测试方法,输出message
assertTrue
断言条件为真,若不满足,方法抛出带有相应信息的AssertionFailedError异常。
assertFalse(a) 测试a是否为false(假),a是一个Boolean数值。
assertFalse
断言条件为假,若不满足,方法抛出带有相应信息的AssertionFailedError异常。
4.3 assertNotNull/Null([String message],Obejct result
Retult= = null/result!=null,中断测试方法,输出message
assertNotNull(a) 测试a是否非空,a是一个对象或者null。
assertNotNull 断言对象不为null,若不满足,方法抛出带有相应信息的AssertionFailedError异常。
4.4 assertSame/NotSame(Object target,Object result)
Traget与result 不指向/指向 同一内存地址(实例),中断测试方法,输出message
assertNotSame(a, b) 测试a和b是否没有都引用同一个对象。
assertNotSame
断言两个引用指向不同对象,若不满足,方法抛出带有相应信息的AssertionFailedError异常。
assertSame 断言两个引用指向同一个对象,若不满足,方法抛出带有相应信息AssertionFailedError异常。
4.5 fail([String message])
中断测试方法,输出message
Fail 让测试失败,并给出指定信息。
5、单元测试的常用的方法
简单案例
实现的类
图片.png
简单案例测试代码
package com.example.controller;
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.core.Is.is;
/ -
Created by wuxiaodong on 2017/8/30.
*/
public class TTest {
@Test
public void plus() throws Exception {
int z=new T().plus(2, 2);
Assert.assertThat(z,is(4));
}@Test
public void minus() throws Exception {
int b=new T().minus(3,1);
Assert.assertThat(b,greaterThan(1));
}
}
图片.png
Junit4常用的方法介绍
前面已经介绍了一部分的方法
5.1、常用的断言介绍
assertEquals(String msg, Object expectRes, Object Res) -------- 用于值判断
判断expectRes.equals(Res) ,表示值等于的判断,失败则抛MSG
assertSame(String msg, Object expectRes, Object Res) -------- 用于地址判断
判断expectRes==Res,表示地址等于的判断,失败则抛MSG
assertTrue(String msg,Boolean result) ----------------------------用于Boolean判断
判断result是true还是false,失败则抛MSG
assertNull(String msg,Object result)-------------------------------用于NULL判断
判断result是否为NULL,失败则抛MSG
fail(String msg);---------------------------------------------------直接中止方法运行
直接中止方法运行,抛出MSG
断言是用来判断被测方法执行的结果与预期结果是否匹配
5.2特殊的处理 (限时测试,异常测试)
Junit提供限时处理的机制。
@Test(timeout=1000) 单位毫秒
当方法用时超过1000毫秒时,此方法会自动中止并执行失败
图片.png
图片.png
loop方法超时报错,div2方法不超时成功
Junit提供异常处理的机制。
@Test(expected=Exception.class) 其中Exception.class可以写的更加具体
执行结果
两个都通过
执行结果
第一个通过 第二个不通过 异常为除数不能为zero
以上已经掌握了一个简单的单元测试的方法,下面会根据工作要求做相对复杂的一个单元测试的工作。
为控制篇幅长度,本篇是关于junit的单元测试,接下来胡对TestNG单元测试框架做介绍和实战演练。
参考:1、https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00140137128705556022982cfd844b38d050add8565dcb9000
2、http://blog.csdn.net/foxman209/article/details/6070430










网友评论