实验简介
理解了代码级接口测试的基本原理与实现方式后,我们再来理解单元测试框架将会非常容易,言下之意也是想告诉大家,对于代码级测试的知识,前面三节的内容,已经足够。所谓的单元测试框架,无非实现了更加简洁的测试驱动和断言,充分利用了编程语言特性帮助测试开发人员提高效率,进而通过丰富一些实用功能,让整个白盒测试过程更加流畅,仅此而已。
实验目的
(1) 掌握JUnit测试框架的配置和使用。
(2) 掌握JUnit测试框架的断言和注解。
(3) 利用JUnit测试框架的高级功能及应用。
实验流程
1. 直接在项目中引入JUnit
(1) 在Eclipse中的项目CodeTest名称上,点击“右键”->“Properties”,打开项目属性对话框,进入“Java Build Path”节点,如图。
(2) 在“Libraries”标签页上,点击“Add Library”按钮,打开“Add Library”对话框,选择“JUnit”,如图。
(3) 点击“ Next”,选择JUnit4的版本,完成即可在代码中使用JUnit框架了,如图。
2. 下载jar包并引入项目中
(1) 访问网址:http://junit.org/junit4/ 下载junit.jar包。
(2) 在项目CodeTest根目录下,创建目录“lib”,当然也可以命名为其它,后续我们的代码中也会导入很多其它的jar包,都统一放置于本目录中。
(3) 在项目中直接导入该jar包,在项目属性对话框,进入“Java Build Path”节点,点击“Add Jar”按钮,将该jar包导入项目中,如图。
3. 使用JUnit 3的语法规则来书写测试脚本
package com.woniuxy.junit;
//导入JUnit相关类和方法 import com.woniuxy.compare.StringHandle; import static org.junit.Assert.assertArrayEquals; import junit.framework.TestCase;
//该测试类必须继承自TestCase类 public class JUnit3Test extends TestCase { // 各个测试方法必须以test开头 public void test_splitString() { StringHandle stringHandle = new StringHandle(); String source = "333,111,222,666"; Integer[] expect = { 333, 111, 222, 666 }; Integer[] actual = stringHandle.splitString(source, ","); assertArrayEquals(expect, actual); // 断言,用于结果比较 }
// 各个测试方法名称必须以test开头 public void test_isNumber() { StringHandle stringHandle = new StringHandle(); String source = "123.45"; boolean actual = stringHandle.isNumber(source); assertTrue(actual); // 断言,用于结果判断 } } |
注意,在使用JUnit 3的方法规则书写测试用例时,必须满足两个基本要求:
(1) 测试类必须继承自junit.framework.TestCase类
(2) 每个测试方法名称必须以test开头,通常我们以test_后面跟上被测方法名称来命名测试方法。
4. 用JUnit 4的规则重写上述测试用例
package com.woniuxy.junit;
import org.junit.Test; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import com.woniuxy.compare.StringHandle;
public class JUnit4Test {
@Test // 只需要声明@Test注解即可 public void splitString() { StringHandle stringHandle = new StringHandle(); String source = "333,111,222,666"; Integer[] expect = {333, 111, 222, 666}; Integer[] actual = stringHandle.splitString(source, ","); assertArrayEquals(expect, actual); // 断言,用于结果判断 } @Test // 只需要声明@Test注解即可 public void isNumber() { StringHandle stringHandle = new StringHandle(); String source = "123.45"; boolean actual = stringHandle.isNumber(source); assertTrue(actual); // 断言用于结果判断 assertEquals(true, actual); // 也可以使用assertEquals断言 } } |
根据JUnit 4的规则,我们可以看到如下一些变化:
(1) 测试类不需要继承TestCase,灵活了很多。
(2) 测试方法名称不需要以test开头,只需要加上@Test注解指明该方法是JUnit测试方法即可。
事实上,在后续的章节中,我们将会看到这两点看似很小的改动,事实上是非常有用的。
5. 执行JUnit测试用例
该结果说明有两个测试用例被执行,并且两个测试用例均成功执行。没有出现错误,如果出现错误或异常,那么我们可以在JUnit视图的下半部分“Failure Trace”中进行定位。
6. 设定用例执行顺序
在上述执行结果中,我们可以看到,isNumber的测试用例在代码中是后于splitString的,但是执行时却是先执行的,那么有没有什么方法可以让我们自己定义执行顺序呢?在相对早期的JUnit版本中,这是无法做到的,只能按照JUnit的方式进行,但是在JUnit 4.11及后续版本中,终于解决了该问题。
我们可以使用JUnit中的一个特殊的注解:@FixMethodOrder来指定测试用例的执行顺序。该注释通过设定一个排序参数即可完成,该排序参数通过MethodSorters对象进行调用,有三种排序方式:
(1) MethodSorters.DEFAULT:默认排序,也就是不使用该注解的情况。
(2) MethodSorters.NAME_ASCENDING:按字母顺序升序排列。
(3) MethodSorters.JVM:按代码中JVM中的加载顺序排列。
所以,针对上述测试代码,如果我们想让splitString先于isNumber的测试用例执行,那么我们可以将代码修改为:
package com.woniuxy.junit;
import org.junit.Test; import org.junit.runners.MethodSorters; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.junit.FixMethodOrder; import com.woniuxy.compare.StringHandle;
// 使用注解,设定执行顺序为按字母表升序执行 @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class JUnit4Test {
@Test // 只需要声明@Test注解即可 public void test_001_splitString() { StringHandle stringHandle = new StringHandle(); String source = "333,111,222,666"; Integer[] expect = {333, 111, 222, 666}; Integer[] actual = stringHandle.splitString(source, ","); assertArrayEquals(expect, actual); // 断言,用于结果判断 } @Test // 只需要声明@Test注解即可 public void test_002_isNumber() { StringHandle stringHandle = new StringHandle(); String source = "123.45"; boolean actual = stringHandle.isNumber(source); assertTrue(actual); // 断言用于结果判断 assertEquals(true, actual); // 也可以使用assertEquals断言 } } |
7. JUnit的断言
我们知道,判断一个测试用例是否通过的过程其实就是一个期望结果与实际结果比较的过程。在JUnit中使用断言(Assert)机制来进行判断,免去了使用if … else …分支语句进行结果判断的麻烦。更可贵的时,如果断言失败,JUnit将会输出期望结果与实际结果的值,便于我们快速定位问题。这就是断言的价值所在。那么,JUnit中都有哪些可用的断言呢?
在JUnit中,有两个断言类,一个是junit.framework.Assert,另外一个是org.junit.Assert。前者是专门为了兼容JUnit 3而刻意保留的,后者则是JUnit 4新增的,如果没有历史遗留原因,建议直接使用JUnit 4断言类中的各类断言,在该类中,主要包含了如下断言方法:
(1) assertTrue(boolean condition)
用法:判断参数是否为布尔“真”,为真则通过,否则失败。
(2) assertFalse(boolean condition)
用法:判断参数是否为布尔“假”,为假则通过,否则失败。
(3) assertEquals(Object expected, Object actual)
用法:判断两个参数值是否相等,支持所有基本数据类型的比较。
(4) assertEquals(double expected, double actual, double delta)
用法:该断言比较特殊,专门用于比较浮点型数据,并使用了误差范围,如以下断言将通过:assertEquals(12.5, 12.3, 0.5),误差0.5表示正负0.5均在允许范围。
(5) assertArrayEquals(Object[] expecteds, Object[] actuals)
用法:比较两个数组是否相同,相同的条件为:长度相同,相同下标的值也相同。
(6) assertNotNull(Object object)
用法:参数为一个非空对象,非空则断言正确,不适用于基本数据类型。
(7) assertNull(Object object)
用法:参数为一个空对象,空则断言正确,不适用于基本数据类型。
(8) assertSame(Object expected, Object actual)
用法:比较两个对象是否相同,相同则断言正确。对象相同的条件是指向同一块内存,该断言不适用于基本数据类型。
(9) assertNotSame(Object unexpected, Object actual)
用法:比较两个对象是否相同,不同则断言正确。
8. assertThat断言
assertThat(T actual, Matcher<T> matcher)
用法:该断言较为特殊,无法直接在JUnit中使用,必须配合Matcher匹配器使用才可以进行断言,Matcher匹配器包含在另外一个扩展框架,叫hamcrest中。我们可以进入其官方页面http://hamcrest.org/下载适用的版本,并导入到项目中便可使用。示例代码如下:
import static org.junit.Assert.*; import org.junit.Test; import static org.hamcrest.Matchers.*;
public class SpecialJUnitUsage { @Test public void assertThat_Test() { double d = 100; assertThat(d, is(100.0)); assertThat(d, lessThan(200.0)); assertThat(d, greaterThan(20.0)); assertThat(d, closeTo(100.0, 1.0)); } } |
9. JUnit的注解
JUnit 4中除了常用的@Test注解外,还包括如下注解:
(1) @BeforeClass: 测试类运行前准备环境,一个测试类在运行测试方法之前运行一次。
(2) @AfterClass: 测试类运行后清除环境,一个测试类在运行完所有测试方法后运行一次。
(3) @Before: 每个测试用例运行前运行,有多少个测试用例,就会运行多少次。
(4) @After: 每个测试用例运行后运行,有多少个测试用例,就会运行多少次。
(5) @Test: 具体的测试用例。