单元测试[1]

利用Java平台最常用的测试框架JUnit,并详细介绍如何编写单元测试。

编写JUnit测试

测试驱动开发TDD

所谓测试驱动开发,是指先编写接口,紧接着编写测试。编写完测试后,我们才开始真正编写实现代码。在编写实现代码的过程中,一边写,一边测,什么时候测试全部通过了,那就表示编写的实现完成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
    编写接口


编写测试


┌─> 编写实现
│ │
│ N ▼
└── 运行测试
│ Y

任务完成

JUnit

一个JUnit测试包含若干@Test方法,并使用Assertions进行断言,注意浮点数assertEquals()要指定delta

Assertion还定义了其他断言方法,例如:

  • assertEquals(expected, actual): 期待结果为true
  • assertTrue(): 期待结果为true
  • assertFalse(): 期待结果为false
  • assertNotNull(): 期待结果为非null
  • assertArrayEquals(): 期待结果为数组并与期望数组每个元素的值均相等

IDEA添加JUnit

新版IDEA如何简单添加JUnit4 jar包单元测试(不用下载)【图文】超详细_一个长不胖的程序YUAN的博客-CSDN博客_idea 添加junit

ctrl+shift+T 可以生成测试单元

(右击test文件夹可以将文件夹改为测试文件夹)

使用Fixture

编写Fixture是指针对每个@Test方法,编写@BeforeEach方法用于初始化测试资源,编写@AfterEach用于清理测试资源;

必要时,可以编写@BeforeAll@AfterAll,使用静态变量来初始化耗时的资源,并且在所有@Test方法的运行前后仅执行一次。

编写Fixture的套路如下:

  1. 对于实例变量,在@BeforeEach中初始化,在@AfterEach中清理,它们在各个@Test方法中互不影响,因为是不同的实例;
  2. 对于静态变量,在@BeforeAll中初始化,在@AfterAll中清理,它们在各个@Test方法中均是唯一实例,会影响各个@Test方法。

大多数情况下,使用@BeforeEach@AfterEach就足够了。只有某些测试资源初始化耗费时间太长,以至于我们不得不尽量“复用”时才会用到@BeforeAll@AfterAll

最后,注意到每次运行一个@Test方法前,JUnit首先创建一个XxxTest实例,因此,每个@Test方法内部的成员变量都是独立的,不能也无法把成员变量的状态从一个@Test方法带到另一个@Test方法。

异常测试

测试异常可以使用assertThrows(),期待捕获到指定类型的异常;

对可能发生的每种类型的异常都必须进行测试。

1
2
3
4
5
6
@Test
void testNegative() {
assertThrows(IllegalArgumentException.class, () -> {
Factorial.fact(-1);
});
}

条件测试

JUnit根据不同的条件注解,决定是否运行当前的@Test方法。

参数化测试

参数化测试和普通测试稍微不同的地方在于,一个测试方法需要接收至少一个参数,然后,传入一组参数反复运行。

JUnit提供了一个@ParameterizedTest注解代替@Test,用来进行参数化测试。

  • @ValueSource()注解传入参数

  • @MethodSource注解,它允许我们编写一个同名的静态方法来提供测试参数

    构造参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @ParameterizedTest
    @MethodSource
    void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
    }

    static List<Arguments> testCapitalize() {
    return List.of( // arguments:
    Arguments.arguments("abc", "Abc"), //
    Arguments.arguments("APPLE", "Apple"), //
    Arguments.arguments("gooD", "Good"));
    }
  • @CsvSource注解,它的每一个字符串表示一行,一行包含的若干参数用,分隔

  • @CsvFileSource注解,指定resources所在CSV文件


  1. 单元测试 - 廖雪峰的官方网站 (liaoxuefeng.com) ↩︎