Junit简介
JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.
Junit 是一个用于编写和运行可重复的测试的一个框架,它是用于单元测试框架体系xUnit的一个实例。
现已更新到 JUnit5 , 本篇文章中使用的是 JUnit4 的4.12 [2017-09-10] 版本。
JUnit 的特点:
提供了注释,以确定测试方法。
提供断言测试预期结果。
提供了测试运行的运行测试。
JUnit测试让您可以更快地编写代码,提高质量
JUnit是优雅简洁。
JUnit测试可以自动运行,检查自己的结果,并提供即时反馈。没有必要通过测试结果报告来手动梳理。
JUnit测试可以组织成测试套件包含测试案例,甚至其他测试套件。
Junit显示测试进度的,如果测试是没有问题条形是绿色的,测试失败则会变成红色。
什么是单元测试?
从名字上看,单元测试是为了测试某一个代码单元而写的测试代码。但是什么叫“一个代码单元”呢?是一个模块、还是一个类、还是一个方法(函数)呢?不同的人、不同的语言,都有不同的理解。一般的定义,尤其是是在OOP领域,是一个类的一个方法。在此,我们也这样理解:单元测试,是为了测试某一个类的某一个方法能否正常工作,而写的测试代码。
Junit4的使用
测试方法的组成
一般而言,一个测试方法主要包括以下三个部分:
- Init,即初始化操作,一般是新建需要测试的类的实例,并且设置一些前提条件,如数据库的连接。
- 执行操作,即调用你要测试的那个方法,并获取运行结果。
- 验证结果,验证结果是否和预期的一致。
相关注解
初始化操作相关 :
@Before
: 如果一个方法被@Before
修饰过了,那么在每个测试方法调用之前,这个方法都会得到调用。@After
: 修饰每个测试方法运行结束之后,会得到运行的方法。@BeforeClass
: 在跑一个测试类的所有测试方法之前,会执行一次被@BeforeClass
修饰的方法。一般用于初始化公共的资源,注意 只能修饰静态方法。@AfterClass
: 执行完所有测试方法之后,会执行一遍被@AfterClass
修饰的方法。一般用于释放公共的资源。与@BeforeClass
注解类似,只能修饰静态方法。
验证相关 :
JUnit为我们提供的验证结果的方法,多数都在 Assert 这个类里面,常用的有:
assertEquals(expected, actual)
:验证expected的值跟actual是一样的,如果是一样的话,测试通过,不然的话,测试失败。如果传入的是object,那么这里的对比用的是equals()。assertEquals(expected, actual, tolerance)
:这里传入的expected和actual是float或double类型的,大家知道计算机表示浮点型数据都有一定的偏差,所以哪怕理论上他们是相等的,但是用计算机表示出来则可能不是,所以这里运行传入一个偏差值。如果两个数的差异在这个偏差值之内,则测试通过,否者测试失败。assertTrue(boolean condition)
:验证contidion的值是true。assertFalse(boolean condition)
:验证contidion的值是false。assertNull(Object obj)
:验证obj的值是null。assertSame(expected, actual)
:验证expected和actual是同一个对象,即指向同一个对象。assertNotNull(Object obj)
:验证obj的值不是null。assertNotSame(expected, actual)
:验证expected和actual不是同一个对象,即指向不同的对象。fail()
:让测试方法失败。
注意:上面的每一个方法,都有一个重载的方法,可以在前面加一个String类型的参数,表示如果验证失败的话,将用这个字符串作为失败的结果报告。
JUnit的其他功能 :
@Ignore
: 忽略被修饰的测试方法。@Test(expected = IllegalArgumentException.class)
: 通过传入expected参数,验证抛出的异常是否符合期望。
实战 :
基本用法
被测试类 Calculate ,包含四个整形之间的简单的四则运算方法 :
public class Calculate {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public int divide(int a, int b){
if (b==0){
throw new IllegalArgumentException("Divisor should not be zero !");
}
return a / b;
}
}
测试类 :
package cn.echo0.calculate;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Calculate Tester.
*
* @author Echo0
* @version 1.0
* @since <pre>Sep 19, 2017</pre>
*/
public class CalculateTest {
private static Calculate calculate;
@BeforeClass
public static void init() throws Exception {
calculate = new Calculate();
}
/**
* Method: add(int a, int b)
*/
@Test
public void testAdd() throws Exception {
int result = calculate.add(719, 520);
Assert.assertEquals(1239, result);
}
/**
* Method: subtract(int a, int b)
*/
@Test
public void testSubtract() throws Exception {
int result = calculate.subtract(520,719);
Assert.assertEquals(-199,result);
}
/**
* Method: multiply(int a, int b)
*/
@Test
public void testMultiply() throws Exception {
int result = calculate.multiply(520,719);
Assert.assertEquals("Result should be 520 !",520,result);
}
/**
* Method: divide(int a, int b)
*/
@Test(expected = IllegalArgumentException.class ,timeout = 1)
// 限定了期望抛出的异常,以及方法运行时间。
public void testDivide() throws Exception {
int result = calculate.divide(520,0);
}
}
运行结果 :
测试套件的使用
测试套件用于同时运行多个测试类 。
使用步骤如下 :
- 使用
@RunWith(org.junit.runners.Suite.class)
修饰套件类 。 - 并使用
@Suite.SuiteClasses()
指定需要运行的测试类 。 - 还需要注意的是,套件类体必须为空 。
如 :
package cn.echo0.calculate;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(org.junit.runners.Suite.class)
@Suite.SuiteClasses({CalculateTest.class,CalculateTest1.class})
public class MySuite {
}
参数化设置
用于同时测试多组数据。
使用步骤如下:
- 使用
@RunWith(Parameterized.class)
更改测试运行器 - 声明变量来存放预期值与结果值
- 声明一个返回值为Collection的公共静态方法,并使用@Parameters进行修饰
- 为测试类声明一个带有参数的公共构造函数,并在其中为之声明变量赋值
package cn.echo0.calculate;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
@RunWith(Parameterized.class)
public class ParameterTest {
int expected;
int input1 ;
int input2 ;
@Parameterized.Parameters
public static Collection<Object[]> initTestData(){
return Arrays.asList(new Object[][]{
{2,1,1},
{3,4,-1}
});
}
public ParameterTest(int expected, int input1, int input2) {
this.expected = expected;
this.input1 = input1;
this.input2 = input2;
}
@Test
public void testAdd(){
Assert.assertEquals(expected,new Calculate().add(input1,input2));
}
}
Idea Junit 插件
这里推荐 Idea 的一个用于生成Junit测试类的插件 :
JUnitGenerator V2.0,真心好用。
安装完成后,在需要生成测试类的类中 使用组合键 Alt + insert 调出 Generate 菜单,选择 JUnitTest 即可 :
最好更改一下测试类的存储路径 :
如,将其路径设置为src/test/java
(maven工程的默认测试目录),则填写的路径应该如下 :
${SOURCEPATH}/../../test/java/${PACKAGE}/${FILENAME}
总结
生命周期
对于Junit测试类 :
@BeforeClass -> @Before -> @Test -> @After -> @AfterClass
对于每个测试方法 :
@Before -> @Test -> @After
注意事项
①测试方法上必须使用@Test进行修饰
②测试方法必须使用public void 进行修饰,不能带任何的参数
③新建一个源代码目录来存放我们的测试代码,即将测试代码和项目业务代码分开
④测试类所在的包名应该和被测试类所在的包名保持一致
⑤测试单元中的每个方法必须可以独立测试,测试方法间不能有任何的依赖
⑥测试类使用Test作为类名的后缀(不是必须)
⑦测试方法使用test作为方法名的前缀(不是必须)
⑧不要在一个测试方法中使用多个断言