Java单元测试工具 Junit4入门使用教程
文章目录
1.序言
JUnit是一个开源的Java单元测试框架,它由Kent Beck和Erich Gamma建立,JUnit4框架中运用了Java5的Annotation接口简化测试用例的编写,JUnit测试框架将测试更加便捷和容易,编写测试代码也是简单、明了,功能强大。
2.前期准备
工具准备:
(1)IntelliJ IDEA(用于创建Java 项目)
(2)安装配置好JDK 1.8
(3)安装配置好Maven
3.创建工程,添加包依赖
- 使用Intellij IDEA 快速新建一个Maven构建的工程
- 在项目工程文件pom.xml中添加Junit4、hamcrest-core、hamcrest-library库依赖
4.JUnit代码实战演示
4.1 实战第一步:
新建编写一个Calculator类,这是一个能够简单实现加减乘除、平方、开方的计算器类,然后对这些功能进行单元测试,这个类并不是很完美,预先故意保留了一些Bug用于演示,代码详情及注释如下:
public class Calculator {
private static int result; // 静态变量,用于存储运行结果
public void add(int n) {
result = result + n; //加法算法
}
public void substract(int n) {
result = result - 1; //预留Bug: 正确的应该是 result =result-n
}
public void multiply(int n) {
// 此方法暂未实现,用于备用
}
public void divide(int n) {
result = result / n;
}
public void square(int n) {
result = n * n;
}
public void squareRoot(int n) {
for (; ;) ; //预留Bug : 死循环
}
public void clear() { // 将result结果清零
result = 0;
}
public int getResult() { //获取最终result的值
return result;
}
}
4.2 实战第二步
生成JUnit测试框架:用右键点击类Calculator弹出菜单,并选择Go to–>Test,勾选如下图所示:
点击后选择Create Test,系统会自动列出你这个类中包含的方法,选择你要进行测试的方法。此例中,我们将对“加、减、乘、除”四个方法进行测试,如下图:
之后系统会自动生成一个新类CalculatorTest,同时自动生成一些空的测试用例,接下来我们要修改我们这些测试用例,修改后结果如下:
package com.wmin.JUnit4TestDemo;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
public class CalculatorTest {
private static Calculator calculator=new Calculator();
@Before
public void setUp() throws Exception {
calculator.clear();
}
@Test //@Test表示自己要测试的方法
public void testAdd() {
calculator.add(2);
calculator.add(3);
System.out.println("输出加法运算结果:"+calculator.getResult());
assertEquals(5, calculator.getResult());
}
@Test
public void testSubstract() {
calculator.add(10);
calculator.substract(2);
System.out.println("输出加法与减法混合后的运算结果:"+calculator.getResult());
assertEquals(8, calculator.getResult());
}
@Ignore("Multiply() Not yet implemented")
@Test
public void testMultiply() {
System.out.println("乘法运算方法未实现!"); //被忽略的方法将不会执行
}
@Test
public void testDivide() {
calculator.add(8);
calculator.divide(2);
System.out.println("输出加法与除法混合后的运算结果:"+calculator.getResult());
assertEquals(4, calculator.getResult());
}
}
4.3 实战第三步
运行测试代码,右键点击CalculatorTest测试类,选择“Run As–>Junit Test”,运行结果图如下;
控制台Junit窗口结果如下:
4.4 实战结果分析
(1)观察步骤4.3中的JUnit运行结果,右上角红色进度条表示表示发现出现错误,表示存在测试案例未通过,如下图,执行了4个测试案例,一个失败,一个忽略;
(2)JUnit输出控制窗口左侧记录每个测试案例的执行结果以及执行消耗时间,右侧窗口
显示测试未通过的测试案例;
(3)测试未通过案例分析:点击不通过案例,输出结果如下:
输出加法与减法混合后的运算结果:9
java.lang.AssertionError:
Expected :8
Actual :9
表示测试案例期望值为“8”,但实际运行结果为“9”,测试未通过;
分析testSubstract() 方法:
@Test
public void testSubstract() {
calculator.add(10);
calculator.substract(2);
System.out.println("输出加法与减法混合后的运算结果:"+calculator.getResult());
assertEquals(8, calculator.getResult());
}
其中Calculator对象分别调用add(10)和sunbstract(2)方法,预期结果result执行这两个方法后为8,实际因为substract方法存在Bug,导致实际结果在执行减法后result值为9,Bug如下:
public void substract(int n) {
result = result - 1; //预留Bug: 正确的应该是 result =result-n
}
5.JUnit4使用教程中级篇
通过JUnit代码实战演练,我们可认识到JUnit 4的基本用法,下面我来熟悉一下第四章节中使用到的Junit4框架及关键字等知识
5.1 引入必要的包
在测试类中用到了JUnit4框架,自然要把相应地Package包含进来。最主要地一个Package就是org.junit.*。把它包含进来之后,绝大部分功能就有了。还有一句话也非常地重要“import static org.junit.Assert.*;”,我们在测试的时候使用的一系列assertEquals方法就来自这个包。大家注意一下,这是一个静态包含(static),是JDK5中新增添的一个功能。也就是说,assertEquals是Assert类中的一系列的静态方法,一般的使用方式是Assert. assertEquals(),但是使用了静态包含后,前面的类名就可以省略了,使用起来更加的方便。
5.2 测试类的声明
大家注意到,我们的测试类是一个独立的类,没有任何父类。测试类的名字也可以任意命名,没有任何局限性。所以我们不能通过类的声明来判断它是不是一个测试类,它与普通类的区别在于它内部的方法的声明,我们接着会讲到。
5.3 创建一个待测试的对象
你要测试哪个类,那么你首先就要创建一个该类的对象。正如上一篇文章中的代码:
private static Calculator calculator = new Calculator();
为了测试Calculator类,我们必须创建一个calculator对象。
5.4 测试方法的声明
在测试类中,并不是每一个方法都是用于测试的,你必须使用“标注”来明确表明哪些是测试方法。“标注”也是JDK5的一个新特性,用在此处非常恰当。我们可以看到,在某些方法的前有@Before、@Test、@Ignore等字样,这些就是标注,以一个“@”作为开头。这些标注都是JUnit4自定义的,熟练掌握这些标注的含义非常重要。
5.5 编写一个简单的测试方法
首先,你要在方法的前面使用@Test标注,以表明这是一个测试方法。对于方法的声明也有如下要求:名字可以随便取,没有任何限制,但是返回值必须为void,而且不能有任何参数。如果违反这些规定,会在运行时抛出一个异常。至于方法内该写些什么,那就要看你需要测试些什么了
例如下方的测试案例
public void testAdd() {
calculator.add(2);
calculator.add(3);
System.out.println("输出加法运算结果:"+calculator.getResult());
assertEquals(5, calculator.getResult());
}
想要测试计算加法的方法是否正确,调用两次add方法,result初始值为0,最终结果为5,如果预期结果与实际结果一致,就通过,反之,测试不通过。
assertEquals(5, calculator.getResult())方法中,第一个参数填写期望结果值,第二个参数填写实际结果值,JUnit会自动比对期望结果与实际结果是否相等并反馈给用户。
5.6 忽略未完成的测试方法
如果一个方法的测试用例已经写好,但是方法体并未实现,在执行测试的时候,一定会失败,但这并不是真正的失败,JUnit提供了一种方法来区别他们,那就是在这种测试函数的前面加上@Ignore标注,表示此方法暂时不参与测试,挂起处理。
5.7 Fixture用法
Fixture的含义就是“在某些阶段必然被调用的代码”。比如我们上面的测试,由于只声明了一个Calculator对象,他的初始值是0,但是测试完加法操作后,他的值就不是0了;接下来测试减法操作,就必然要考虑上次加法操作的结果,因此我们需要再所有测试用例执行前清空其他干扰,@Before的作用就是使所有测试用例执行前,都必须执行的一个“前置”步骤,同理还有@After,表示所有测试用例执行完毕后都要执行@After中的收尾工作。
6.JUnit4 使用教程高级篇
6.1 测试函数执行效率
6.2 高级Fixture
第5.7章节讲述@Before和@After方法,但是@Before和@After是在测试类中每一个测试用例方法之前和之后都要执行一篇,遇到复杂的程序或耗时较长的程序时,比较费时间,JUnit提供了@BeforeClass和@AfterClass方法,表示每个测试类只在初始化时执行@BeforeClass当中的内容,当所有测试用例执行完毕之后,再执行@AfterClass方法。
例如:
@BeforeClass
public void setUp() throws Exception { //异常判断
calculator.clear();
}
则表示方法在类开始时执行一次,后续每个TestCase执行时,不在执行这个方法。
注意:每个测试类只能有一个方法被标注为@BeforeClass或@AfterClass,并且该方法必须是 Public和Static的
6.3 限时测试
如果一个测试案例执行时间较长,或者出现死循环不停执行时,我们需要控制测试案例执行时间,只需在@Test中添加时间参数,Timeout 参数表明了你要设定的时间,单位为毫秒
例如下面高级测试类AdvancedTest.java中
package JunitTest;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class AdvancedTest {
private static Calculator calculator = new Calculator(); //创建一个Calculator对象
@Before
public void setUp() throws Exception {
calculator.clear();
}
@Test(timeout=2000)
public void testSquareRoot() {
//fail("Not yet implemented");
calculator.squareRoot(4);
assertEquals(2, calculator.getResult());
}
}
因原始类Calculater中的SquareRoot()方法存在无限循环缺陷,设置testSquareRoot()方法超时参数后,方法执行2秒后即停止,控制台输出结果如下:
6.4 异常测试
JAVA中的异常处理也是一个重点,因此你经常会编写一些需要抛出异常的函数。那么,如果你觉得一个函数应该抛出异常,这当然是Bug ,并且JUnit也考虑到了这一点,来帮助我们找到这种 Bug 。例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除以0异常”类似的提示。因此,我们很有必要对这些进行测试。
@Test(expected=ArithmeticException.class )
//@Test
public void divideByZero() {
calculator.divide( 0 );
}
6.5 Runner( 运行器 )
当你把测试代码提交给JUnit框架后,框架如何来运行你的代码呢?答案就是Runner 。在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。
运行器使用@RunWith标注,并且把你所指定的 Runner 作为参数传递给它。另外一个要注意的是, @RunWith 是用来修饰类的,而不是用来修饰函数或者方法的。如果不指定Runner,则会使用系统默认的TestClassRunner,例如下方的两个代码效果是一样的
public class CalculatorTest {
…
}
和
@RunWith(TestClassRunner. class )
public class CalculatorTest {
…
}
6.6 参数化测试
在进行单元测试案例设计时,我们对同一方法的参数取值可能会存在多种,例如Calculater.java中求平法这个方法,参数取值为整数类型,取值可能有正数、负数、0三种情况,测试时我们可以写三个测试案例,三个测试方法对这个求平方的方法进行校验,如下所示:
public void square1() {
…
}
public void square2() {
…
}
public void square3() {
…
}
如果要测试的方法中,参数取值有7、8种甚至十几种,这种方式写测试案例显示不行,为了简化类似的测试, JUnit4 提出了“参数化测试”的概念,只写一个测试函数方法,把这若干种情况作为参数传递进去,一次性的完成所有输入参数的测试。代码如下:
package JunitTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.util.Arrays;
import java.util.Collection;
@RunWith(Parameterized. class )
public class SquareTest{
private static Calculator calculator = new Calculator();
private int param;
private int result;
@Parameters
public static Collection data(){
return Arrays.asList(new Object[][] {
{2, 4},
{0, 0},
{-3,-9},
});
}
// 构造函数,对变量进行初始化
public SquareTest(int param, int result){
this .param = param;
this .result = result;
}
@Test
public void square(){
calculator.square(param);
assertEquals(result, calculator.getResult());
}
}
参数化测试操作步骤及注意事项:
(1)参数化测试时,你要为这种测试专门生成一个新的类,而不能与其他测试共用同一个类;
(2)然后,你要为这个类指定一个Runner,而不能使用默认的Runner了,因为特殊的功能要用特殊的Runner嘛。@RunWith(Parameterized.class)这条语句就是为这个类指定了一个ParameterizedRunner。
(3)定义一个待测试的类,并且定义两个变量,一个用于存放参数,一个用于存放期待的结果。接下来,定义测试数据的集合,也就是上述的data()方法,该方法可以任意命名,但是必须使用@Parameters标注进行修饰,注意其中的数据,是一个二维数组,数据两两一组,每组中的这两个数据,一个是参数,一个是你预期的结果,比如我们的第一组{2, 4},2就是参数,4就是预期的结果。这两个数据的顺序前后任意,但是下一步构造方法参数要跟这个顺序匹配;
(4)构造函数,其功能就是对先前定义的两个参数进行初始化。 在这里你可要注意一下参数的顺序了,要和上面的数据集合的顺序保持一致。如果前面的顺序是{参数,期待的结果},那么你构造函数的顺序也要是“构造函数(参数, 期待的结果)”,反之亦然。最后就是写一个简单的测试案例。
6.7 打包测试
实际项目中,我们会写出很多很多个测试类,如果这些测试类必须一个一个的执行,也是比较麻烦的事情;JUni为我们提供了打包测试的功能,将所有需要运行的测试类集中起来,一次性的运行完毕,大大的方便了我们的测试工作。具体代码如下:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite. class )
@Suite.SuiteClasses( {
CalculatorTest. class ,
SquareTest. class
} )
public class AllCalculatorTests {
.......
}
注意事项:
(1)打包测试需要一个特殊的Runner,在@RunWith标注传递一个参数Suite.class;
(2)同时,我们还需要另外一个标注@Suite.SuiteClasses ,来表明这个类是一个打包测试类,我们把需要打包的类作为参数传递给该标注就可以了,如上述例子中,将CalculatorTest和SquareTest两个类打包传递过去。
(3)打包测试类任意取名就行,类里面的内容为空即可。
7.相关源码
上一篇: php学习代码杂记,php代码杂记
下一篇: 自定义PHP分页函数_php基础