一、概述
单元测试(Unit Testing)是指对软件中的最小可测试单元进行检查和验证,一般要根据实际情况去判定其具体含义,比如在 Java 语言中,单元可以指一个类或一个方法。JUnit 是一个 Java 语言简单、开源的单元测试框架,在 Java 开发中比较流行且常用的测试框架,希望读者能通过这篇文章来深入了解 JUnit 的特性与使用。
二、JUnit 基本用法
1. 测试运行器
JUnit 测试的运行器可以指定当前测试通过怎样的方式去运行,也可以配置运行的上下文,可以在类或父类上使用 @RunWith
注解来指定一个特定的运行器,而不是使用 JUnit 默认的运行器,很多第三方框架整合 JUnit 框架,一般都会有自定义的 Runner
接口实现。
@RunWith(JUnit4.class) public class DemoTest { @Test public void assertTest() { // do something } }
下面是 JUnit 专门提供的运行器,在后面我们会详细讲解到它们的用法:
Suite
:可以手动构建多个类的测试套件的标准运行器Parameterized
:可以为测试方法以参数的形式提供测试数据的标准运行器Categories
:可以手动指定一组测试,配置包含或不包含一些测试方法或类的运行器
2. 断言与假设
2.1 断言
Java 提供了关键字 assert
来实现断言,断言功能可以通过 Java 运行参数来启用或者禁用,当断言判断失败时,会抛出 AssertionError
错误,它的用法如下:
assert condition; assert condition : message;
JUnit 为所有基本类型、对象和数组类型提供了一组断言方法,这些方法都位于 Assert
类中,当断言判断失败时,会抛出 AssertionError
错误,示例代码如下:
public class DemoAssertTest { @Test public void assertTest() { int a = 5; assert a < 0; assert a < 0 : "整数为正数"; } @Test public void assertJUnitTest() { int a = 2; Object expected = new Object(); Object actual = new Object(); assertTrue("条件为假", a > 1); assertFalse("条件为真", a > 4); assertEquals("整数与期望的数不相等", 100, 80); assertNotEquals("整数与期望的数相等", 100, 80); assertArrayEquals("", new Integer[]{1, 2, 3, 4, 5}, new Integer[]{1, 2, 3, 4, 5}); assertNotNull("", actual); assertNull("", actual); assertSame("", expected, actual); assertNotSame("", expected, actual); assertThat(actual, is(expected)); } }
断言方法的错误提示消息参数都是可选的,一般断言方法的参数是布尔类型,来进行条件判断,也有判断实际值与期望值是否相符的。期望值不一定是确定的值,例如在 assertThat
方法中,参数 Matcher
接口提供了一个匹配的操作,用来判断实际值是否符合期望。
2.2 假设
假设与断言类似,它一般用于测试代码之前,判断测试的参数和执行环境条件,可以避免一些不关注的点来影响测试结果,Assume
类提供了与断言类似的方法,当假设判断失败时,会抛出 AssumptionViolatedException
异常,示例代码如下:
public class DemoAssumeTest { @Test public void filenameIncludesUsername() { // 假设前提条件 assumeThat(File.separatorChar, is('/')); assertThat(new User("optimus").configFileName(), is("configfiles/optimus.cfg")); } @Test public void correctBehaviorWhenFilenameIsNull() { // 假设前提条件 assumeTrue(bugFixed("13356")); // bugFixed is not included in JUnit assertThat(parse(null), is(new NullDocument())); } @Test public void test() { int a = 2; Object object = new Object(); assumeTrue("Assume true", true); assumeFalse("Assume false", false); assumeNotNull(object); assumeNoException("Assume no exception", new RuntimeException()); assumeThat("Assume that", a, is(3)); } }
断言方法的错误提示消息参数都是可选的,与断言的方法参数类似,例如在 assertThat
方法中,参数 Matcher
接口提供了一个匹配的操作,用来判断实际值是否符合期望。
3. 套件测试
套件测试可以手动构建多个测试类一起运行,它使用 Suite
运行器来实现,假设我们存在如下三个测试类:
public class Test1 { @Test public void test() { System.out.println("Test1.test"); } @Test public void test2() { System.out.println("Test1.test2"); } } public class Test2 { @Test public void test() { System.out.println("Test2.test"); } @Test public void test2() { System.out.println("Test2.test2"); } } public class Test3 { @Test public void test() { System.out.println("Test3.test"); } }
新建一个测试类并使用 @RunWith(Suite.class)
注解来指定运行器,然后使用 @SuiteClasses(TestClass1.class, ...)
注解来指定包含的测试类,运行此类就会运行套件包含的所有测试类。
@RunWith(Suite.class) @Suite.SuiteClasses({Test1.class, Test2.class, Test3.class}) public class DemoSuiteTest { }
4. 分类测试
分类测试可以指定将某些测试类或测试方法进行分类,然后运行其中的某些类别的测试方法,或者不运行其中的某些类别的测试方法,它使用 Categories
运行器来实现,首先我们定义两个接口来标记类别:
public interface FastTests { } public interface SlowTests { }
再定义两个测试类示例,使用 @Category
注解来标记测试类或测试方法的分类:
public class Test1 { @Test public void test() { System.out.println("Test1.test"); } @Test @Category(FastTests.class) public void test2() { System.out.println("Test1.test2"); } } @Category({FastTests.class, SlowTests.class}) public class Test2 { @Test public void test() { System.out.println("Test2.test"); } @Test public void test2() { System.out.println("Test2.test2"); } }
分类测试是套件测试的扩展,运行器类也是其子类,所以我们在运行分类测试的时候,首先得使用 @Suite.SuiteClasses
注解来指定需要分类的所有测试类,然后使用 @RunWith(Categories.class)
来指定分类测试运行器,@Categories.IncludeCategory
注解指定运行的类别,@Categories.ExcludeCategory
指定不运行的类别,示例如下:
@RunWith(Categories.class) @Categories.IncludeCategory(FastTests.class) @Categories.ExcludeCategory(SlowTests.class) @Suite.SuiteClasses({Test1.class, Test2.class, Test3.class}) public class DemoCategoriesTest { }
5. 顺序测试
JUnit 支持指定测试方法的执行顺序,在一个测试类上,可以使用 @FixMethodOrder
注解来指定,通过选择不同的方法 Comparator
接口实现来选择执行顺序。
@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class DemoOrderTest { @Test public void testB() { System.out.println("DemoOrderTest.testB"); } @Test public void testA() { System.out.println("DemoOrderTest.testA"); } @Test public void testC() { System.out.println("DemoOrderTest.testC"); } }