一.JUnit基本介绍
1、简介
JUnit是一个开放源码的、Java语言的单元测试框架,用于测试期望结果的断言(Assertion);由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。多数Java的开发环境都已经集成了JUnit作为单元测试的工具,如Eclipse。
测试一般分为:单元测试、集成测试(主要看一块代码加进去后,系统会不会有问题)、验收测试和压力测试。单元测试的最基本的一个功能是能进行自动化测试。单元测试都是通过断言的方式来确定结果是否正确,即是用Assert。
2、优点
junit是在极限编程和重构(refactor)中被极力推荐使用的工具,因为在实现自动单元测试的情况下可以大大的提高开发的效率。
极限编程:要求在编写代码之前先写测试,这样可以强制你在写代码之前好好的思考代码(方法)的功能和逻辑,否则编写的代码很不稳定,那么你需要同时维护测试代码和实际代码,这个工作量就会大大增加。因此在极限编程中,基本过程是这样的:构思-> 编写测试代码-> 编写代码-> 测试,而且编写测试和编写代码都是增量式的,写一点测一点,在编写以后的代码中如果发现问题可以较快的追踪到问题的原因,减小回归错误的纠错难度。
重构:其好处和极限编程中是类似的,因为重构也是要求改一点测一点,减少回归错误造成的时间消耗。
3、Junit3与Junit4
在junit3中,如果某个类是测试类,必须将其继承类TestCase,如果某个方法是测试方法,必须让这个方法以testXX开头,如果希望指定某个测试方法运行之前运行某个初始化方法,这个方法的名称必须是setUp,如果希望在某个测试方法运行之后运行某个释放资源的方法,这个方法的名称必须是tearDown。
在junit4中,一个POJO类就是一个测试类,测试方法通过@Test来标识,初始化方法通过@Before来标识,释放资源的方法通过@After来标识,但是为了让junit4的测试类在junit3中也可以使用,习惯于把初始化方法命名为setUp,释放资源的方法命名为tearDown。Test中的测试方法一般以Test来开始。其中标识为Before注解的方法,每次运行测试类,都会执行标识为@After与@Before的方法。
二.JUnit单元测试
由于JUnit4引用了很多Annotation注解以后,用JUnit进行单元测试比较方便,主要关键点在于:注解,断言的使用;
以下为测试的示例:
待测试类
package com.test;
/**
* 计算类
* @author helen.
* @Time 2016年5月13日
* @Version 1.0
*/
public class Calculator {
public int add(int a, int b) {
System.out.println("=======正在执行加法");
return a + b;
}
public int minus(int a, int b) {
System.out.println("=======正在执行减法");
return a - b;
}
public int square(int n) {
System.out.println("=======正在执行平方计算");
return n * n;
}
//Bug : 死循环
public void squareRoot(int n) {
System.out.println("=======正在执行死循环的方法");
for(; ;)
;
}
public int multiply(int a, int b) {
System.out.println("=======正在执行乘法");
return a * b;
}
public int divide(int a, int b) throws Exception {
System.out.println("=======正在执行除法");
if (0 == b) {
throw new Exception("除数不能为零");
}
return a / b;
}
}
单元测试类
package com.test;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import static org.hamcrest.Matchers.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
/**
* 计算类单元测试
* @author helen.
* @Time 2016年5月13日
* @Version 1.0
*/
public class CalculatorTest {
private Calculator cal = new Calculator();
/**
* 注意
* 必须为静态方法static...因为方法将在类被装载的时候就被调用(那时候还没创建实例)
*
* @author helen, 2016年5月13日.
*/
@BeforeClass
public static void before()
{
System.out.println("global");
}
@AfterClass
public static void after() {
System.out.println("global destroy");
}
@Before
public void setUp() throws Exception {
System.out.println("one test begin");
}
@After
public void tearDown() throws Exception {
System.out.println("one test end");
}
@Test
@Ignore
public void testAdd() {
int result = cal.add(1, 2);
Assert.assertEquals(30, result);
}
@Test
public void testMinus() {
int result = cal.minus(5, 2);
Assert.assertThat(result, greaterThan(2));
}
@Test
public void testMultiply() {
int result = cal.multiply(4, 2);
Assert.assertEquals(8, result);
}
@Test(timeout = 1000) // 单位为毫秒
public void testSquareRoot() {
cal.squareRoot(4);
}
@Test(expected = Exception.class)
public void testDivide() throws Exception {
cal.divide(4, 0);
}
}
运行测试用:在Eclipse里Run As -> JUnit Test,运行测试类,Eclipse的JUnit的View显示以及控制台的输出如下截图:
可以看到,CalculatorTest类中总共有5个测试用例,ignore了一个,3个测试用例通过,testSquareRoot测试不通过(因为超时),所以整个的测试结果飘红了。
三.JUnit使用详解
1、注解说明
@Test:
表明该方法是一个测试方法
@BeforeClass 和 @AfterClass:
测试用例初始化时执行 @BeforeClass方法,当所有测试执行完毕之后,执行@AfterClass进行收尾工作。标注、@BeforeClass 和 @AfterClass的方法必须是static的,因为方法将在类被装载的时候就被调用,那时候还没创建测试对象实例。
@Before:
使用了该元数据的方法在每个测试方法执行之前都要执行一次。
@After:
使用了该元数据的方法在每个测试方法执行之后要执行一次。
@Test(expected=*.class) :
通过@Test元数据中的expected属性验证是否抛出期望的异常,expected属性的值是一个异常的类型,如果抛出了期望的异常,则测试通过,否则不通过。
@Test(timeout=xxx):
该元数据传入了一个时间(毫秒)给测试方法,如果测试方法在制定的时间之内没有运行完,则测试也失败。
@Ignore:
该元数据标记的测试方法在测试中会被忽略。同时可以为该标签传递一个String的参数,来表明为什么会忽略这个测试方法。比如:@lgnore("该方法还没有实现"),在执行的时候,仅会报告该方法没有实现,而不会运行测试方法。
2、常用断言
在test方法内除了使用Assert的assertEquals()方法外,还能使用assertFalse()、assertTrue()、assertNull()、assertNotNull()、assertSame()、assertNotSame()等断言函数。而且如果使用的是Junit4,结合Hamcrest,使用
assertThat([value], [matcher statement])方法可以实现更灵活的断言判断(前提是引入hamcrest的jar包)。
如果不需要用到assertThat,则只需要用Eclipse自带的junit4工具即可;如果需要用到assertThat,则最好不要Eclipse自带的工具,重新通过maven方式引入jar。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
</dependency>
四.assertThat用法(断言使用)
Hamcrest框架和assertThat:
1.JUnit4.4引入了Hamcrest框架,Hamcest提供了一套匹配符Matcher,这些匹配符更接近自然语言,可读性高,更加灵活;
2.使用全新的断言语法:assertThat,结合Hamcest提供的匹配符,只用这一个方法,就可以实现所有的测试;
3.assertThat语法如下:
assertThat(T actual, Matcher<T> matcher);
assertThat(String reason, T actual, Matcher<T> matcher);
其中actual为需要测试的变量,matcher为使用Hamcrest的匹配符来表达变量actual期望值的声明;
4.注意事项:
a.必须导入JUnit4.4之后的版本才能使用assertThat方法;
b.不需要继承TestCase类,但是需要测试方法前必须加“@Test”。
常用:
// is匹配符表明如果前面待测的object等于后面给出的object,则测试通过
assertThat( testedObj, is( object) );
// containsString匹配符表明如果测试的字符串包含指定的子字符串则测试通过
assertThat( testedString, containsString( "developerWorks" ) );
// greaterThan匹配符表明如果所测试的数值testedNumber大于16.0则测试通过
assertThat( testedNumber, greaterThan(16.0) );
// closeTo匹配符表明如果所测试的浮点型数testedDouble在20.0±0.5范围之内则测试通过
assertThat( testedDouble, closeTo( 20.0, 0.5 ) );
//hasItem匹配符表明被测的迭代对象含有元素element项则测试通过assertThat(iterableObject, hasItem (element));
一般匹配符:
1、assertThat( testedNumber, allOf( greaterThan(8), lessThan(16) ) );
注释: allOf匹配符表明如果接下来的所有条件必须都成立测试才通过,相当于“与”(&&)
2、assertThat( testedNumber, anyOf( greaterThan(16), lessThan(8) ) );
注释:anyOf匹配符表明如果接下来的所有条件只要有一个成立则测试通过,相当于“或”(||)
3、assertThat( testedNumber, anything() );
注释:anything匹配符表明无论什么条件,永远为true
4、assertThat( testedString, is( "developerWorks" ) );
注释: is匹配符表明如果前面待测的object等于后面给出的object,则测试通过
5、assertThat( testedString, not( "developerWorks" ) );
注释:not匹配符和is匹配符正好相反,表明如果前面待测的object不等于后面给出的object,则测试通过
字符串相关匹配符
1、assertThat( testedString, containsString( "developerWorks" ) );
注释:containsString匹配符表明如果测试的字符串testedString包含子字符串"developerWorks"则测试通过
2、assertThat( testedString, endsWith( "developerWorks" ) );
注释:endsWith匹配符表明如果测试的字符串testedString以子字符串"developerWorks"结尾则测试通过
3、assertThat( testedString, startsWith( "developerWorks" ) );
注释:startsWith匹配符表明如果测试的字符串testedString以子字符串"developerWorks"开始则测试通过
4、assertThat( testedValue, equalTo( expectedValue ) );
注释: equalTo匹配符表明如果测试的testedValue等于expectedValue则测试通过,equalTo可以测试数值之间,字
符串之间和对象之间是否相等,相当于Object的equals方法
5、assertThat( testedString, equalToIgnoringCase( "developerWorks" ) );
注释:equalToIgnoringCase匹配符表明如果测试的字符串testedString在忽略大小写的情况下等于"developerWorks"则测试通过
6、assertThat( testedString, equalToIgnoringWhiteSpace( "developerWorks" ) );
注释:equalToIgnoringWhiteSpace匹配符表明如果测试的字符串testedString在忽略头尾的任意个空格的情况下等
于"developerWorks"则测试通过,注意:字符串中的空格不能被忽略
数值相关匹配符
1、assertThat( testedDouble, closeTo( 20.0, 0.5 ) );
注释:closeTo匹配符表明如果所测试的浮点型数testedDouble在20.0±0.5范围之内则测试通过
2、assertThat( testedNumber, greaterThan(16.0) );
注释:greaterThan匹配符表明如果所测试的数值testedNumber大于16.0则测试通过
3、assertThat( testedNumber, lessThan (16.0) );
注释:lessThan匹配符表明如果所测试的数值testedNumber小于16.0则测试通过
4、assertThat( testedNumber, greaterThanOrEqualTo (16.0) );
注释: greaterThanOrEqualTo匹配符表明如果所测试的数值testedNumber大于等于16.0则测试通过
5、assertThat( testedNumber, lessThanOrEqualTo (16.0) );
注释:lessThanOrEqualTo匹配符表明如果所测试的数值testedNumber小于等于16.0则测试通过
collection相关匹配符
1、assertThat( mapObject, hasEntry( "key", "value" ) );
注释:hasEntry匹配符表明如果测试的Map对象mapObject含有一个键值为"key"对应元素值为"value"的Entry项则测试通过
2、assertThat( iterableObject, hasItem ( "element" ) );
注释:hasItem匹配符表明如果测试的迭代对象iterableObject含有元素“element”项则测试通过
3、assertThat( mapObject, hasKey ( "key" ) );
注释: hasKey匹配符表明如果测试的Map对象mapObject含有键值“key”则测试通过
4、assertThat( mapObject, hasValue ( "key" ) );
注释:hasValue匹配符表明如果测试的Map对象mapObject含有元素值“value”则测试通过