一、概述
JUnit是一个Java语言的单元测试框架。JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。
JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。Junit是一套框架,继承TestCase类,就可以用Junit进行自动测试了。
Junit促进了“先测试后编码”的理念,签掉建立测试数据的一段代码,可以先测试然后再应用,增加程序员产量和稳定性,可以减少程序员压力和花费在排错上的时间。
使用JUnit,需要在maven的pom文件中添加Junit的依赖(非maven项目导入Junitjar包):
<dependencies>
[...]
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
[...]
</dependencies>
或者springboot项目中(springcloud微服务项目)maven的pom文件中导入springboot的测试依赖综合依赖也可涵盖junit的依赖:
<dependencies>
[...]
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
[...]
</dependencies>
或者:
<dependencies>
[...]
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
[...]
</dependencies>
二、框架构成
Junit的测试类包含测试方法,测试方法带有断言,当需要一次运行多个测试类时就需要创建测试集(test suit或者Suite来打包多个测试类),Runner形成Junit框架的核心。
包含一个或多个测试方法的类为测试类,测试方法即用@Test注解的方法,使用一个测试类可以把类的行为归为一组。
Junit在调用每个@Test方法前为测试类创建一个新的实例,提供测试方法之间的独立性。
测试类提供了在所有测试调用指令前发起的setUp方法,在所有测试方法运行完后的tearDown方法,便于测试各种复杂场景。
把多个测试类归为一组方式为测试集。
如果没有为测试类定义一个测试集,那么junit默认会自动提供一个测试集包含测试类的所有测试。通常使用测试集将同一个包中测试类整合为一个测试集百年与运行。
在Junit中** @RunWith和@Suite **都被组合用作运行测试集。
测试集写法遵循原则:
1.创建一个空类作为测试集的入口
2.使用RunWith和SuiteClass来修饰这个空类
3.将Suite作为参数传给RunWith,@RunWith(Suite.class)
4.将需要集合到一起运行的测试类作为数组传递给SuiteClassess,@SuiteClassess({xxx1Test.class,xxx2Test.class…})
@RunWith(Suite.class)
@Suite.SuiteClasses(value={XXX1Test.class,XXX2Test.class...})
public class AllOrderTestSuite{
}
要运行一个基础的测试类,不需要特别配置Junit会为你使用默认测试运行器来管理你的测试类的生命周期,包括创建类、调用测试方法等。BlockJUnit4ClassRunner是在没指定其它Runner的情况下的默认Runner。
但是默认的测试运行器每次只能运行一个测试方法,且在复杂业务时方法中的对象会互相依赖引用时默认的Junit运行器无法管理复杂的调用关系,因此Junit提供了各种测试运行器。使用@RunWith可以指定运行测试类的运行器,常见的运行器有如下几种:
1.JUnit4运行器:当前版本的JUnit中调用默认的JUnit 4运行器
2.Suite运行器:能同时运行多个测试类的所有测试方法的套件运行器
3.SpringJUnit4ClassRunner:测试运行于Spring环境,在测试类中可以使用Spring的依赖注入事务等Spring框架特有的功能
4.Parameterized:参数测试
5.MockitoJUnitRunner:自动地初始化mock,通常作为PowerMock测试类的运行器
相关参考:
Runner启动代码分析
各常用运行器及运行器使用区别
三、常用API
@Test
public void testAssertionsApi(){
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = null;
String str4 = "abc";
String str5 = "abc";
int val1 = 5;
int val2 = 6;
String[] array1 = {"one","two","three"};
String[] array2 = {"one","two","three"};
String[] array3 = {"one","three","two"};
//判断两个对象是否相同(使用对象的equal方法比较,A.equals(B))
assertEquals(str1,str2);
assertEquals(str1,str4);
//判断两个对象是否为同一实例(A == B,引用指向同一对象)
assertSame(str4,str5);
assertNotSame(str1,str4);
//判断是否为空
assertNull(str3);
assertNotNull(str1);
//判断条件是否成立
assertTrue(val1 < val2);
assertFalse(val1 > val2);
//判断数组是否相同(判断顺序为 ary1 == ary2 => 是否为空 =》 长度是否相等 =》 遍历同一下标的对象是否相等 ...ary[i].equal(ary2[i]))
assertArrayEquals(array1,array2);
//assertArrayEquals(array1,array3);失败
//assertThat(T,March()),注意导包时使用org.hamcrest
assertThat(val1,allOf(greaterThan(2),lessThan(8)));
//fail()让测试失败
// 一般用于测试不应达到的错误分支,正常测试用例不使用
}
-
TestCase测试案例
在进行单元测试的时候,在JUNIT4之前,我们需要测试的代码所在的类一般都需要直接或者间接继承自TestCase,TestCase继承自Assert类实现了Test接口,因此TestCase中可以直接使用Assert中的相关方法,使用Test接口的run执行案例。
需要注意在我们这个类的测试流程,假设我们创建的TestCase子类中有两个测试用例testMethod1和testMethod2,对于我们类中的两个测试用例testMethod1和testMethod2,都会分别创建一个新的TestCase子类对象,并引起TestCase中的setUp和tearDown函数分别执行一遍,因此,在进行单元测试的过程中,我们可以在setUp当中进行一些初始化操作(如类的某些属性的赋值操作),在tearDown中进行一些扫尾工作(如类中某些对象所持有资源的释放)。
TestCase类中常用重要方法如下:- countTestCases():被run执行的测试案例计数
- createResult():创建默认的TestResult
- getName():获取TestCase的名称
- run()/run(TestResult result):运行测试案例并收集结果
- setUp():测试案例执行前初始化
- tearDown():测试案例执行完成后
- toString():返回测试案例的一个字符串表示
tips:TestCase调用数序为:setUp->testXXX1->tearDown;setUp->testXXX2->tearDown;…
执行TestCase
1.使用JUnitCore.run来执行并分析结果:
public static void main(String[] args){
Result result = JUnitCore.run(xxxMyTest1.class);
//输出测试结果
for(Failure fail: result.getFailures()){
Sytem.out.pringln(fail.toString());
}
}
2.使用TestCase自带的run运行测试案例(每次必须指定测试方法名,否则会报找不到对应测试方法的错):
public class JunitApiTest extends TestCase {
public void testAssertionsApi(){
....
}
}
public static void main(String[] args) {
JunitApiTest test = new JunitApiTest();
test.setName("testAssertionsApi");
TestResult result = test.run();
System.out.println(result.toString());
}
3.idea中在测试类上直接执行测试用例(继承TestCase后类和testXXX方法变为可执行的测试案例)
在JUNIT4中可以直接使用@Test注解更灵活方便的创建和执行单元测试测试案例,无需继承TestCase,Junit4虽然也支持TestCase这种写法,但是TestCase更适用与需要与JUnit 3(和/或Java 5之前的Java版本)兼容的情况,否则更建议使用使用JUNIT4的@Test写法,更容易使用各种开发工具执行和调试,也有了更多的拓展。
tips:新旧版本测试用例区别与使用请参考此博文
TestResult类收集所有执行测试案例的结果。TestResult使用了java设计模式的Collecting Parameter模式,TestResult是收集很多运行的Test的运行结果,这里就需要对于这些运行结果进行管理,则TestResult类定义了如下相关的方法:
public synchronized void addError(Test test, Throwable t) //新增一个错误到ArrayList<TestFailure>。
public synchronized void addFailure(Test test, AssertionFailedError t) //新增一个失败到ArrayList<TestFailure>。
public synchronized void addListener(TestListener listener) //在一个test中注册一个监听器到ArrayList<TestListener>,//这个监听器就是TestListener,实现类是TestRunner。
public synchronized void removeListener(TestListener listener) //从一个test中取消这个监听器。
private synchronized List<TestListener> cloneListeners() // 克隆一批监听器。
tips:TestResult详细方法分析参考此文
四、常用注解
将一个普通方法修饰成一个测试方法,
@Test(excepted=xx.class): xx.class表示异常类,表示测试的方法抛出此异常时,认为是正常的测试通过
@Test(timeout=毫秒数) :测试方法执行时间是否符合预期
会在所有的方法执行前被执行,static方法
会在所有的方法执行之后进行执行,static方法
会在每一个测试方法被运行前执行一次
会在每一个测试方法运行后被执行一次
所修饰的测试方法会被测试运行器忽略
可以配置更改测试运行器org.junit.runner.Runner
用于使用参数化功能。
tips:注解执行顺序:
beforeClass->before->testXXX1->after;before->testXXX2->after;…afterClass;
其他相关
junit版本升级到了5了,junit5比junit4做了更多模块化的框架,添加了更多的更丰富的注解。如有更复杂测试需求,自行学习。