参数化测试

如果待测试的输入和输出是一组数据:可以把测试数据组织起来用不同的测试数据调用相同的测试方法

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

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

假设我们想对Math.abs()进行测试,先用一组正数进行测试:

  1. @ParameterizedTest
  2. @ValueSource(ints = { 0, 1, 5, 100 })
  3. void testAbs(int x) {
  4. assertEquals(x, Math.abs(x));
  5. }

再用一组负数进行测试:

  1. @ParameterizedTest
  2. @ValueSource(ints = { -1, -5, -100 })
  3. void testAbsNegative(int x) {
  4. assertEquals(-x, Math.abs(x));
  5. }

注意到参数化测试的注解是@ParameterizedTest,而不是普通的@Test

实际的测试场景往往没有这么简单。假设我们自己编写了一个StringUtils.capitalize()方法,它会把字符串的第一个字母变为大写,后续字母变为小写:

  1. public class StringUtils {
  2. public static String capitalize(String s) {
  3. if (s.length() == 0) {
  4. return s;
  5. }
  6. return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
  7. }
  8. }

要用参数化测试的方法来测试,我们不但要给出输入,还要给出预期输出。因此,测试方法至少需要接收两个参数:

  1. @ParameterizedTest
  2. void testCapitalize(String input, String result) {
  3. assertEquals(result, StringUtils.capitalize(input));
  4. }

现在问题来了:参数如何传入?

最简单的方法是通过@MethodSource注解,它允许我们编写一个同名的静态方法来提供测试参数:

  1. @ParameterizedTest
  2. @MethodSource
  3. void testCapitalize(String input, String result) {
  4. assertEquals(result, StringUtils.capitalize(input));
  5. }
  6. static List<Arguments> testCapitalize() {
  7. return List.of( // arguments:
  8. Arguments.arguments("abc", "Abc"), //
  9. Arguments.arguments("APPLE", "Apple"), //
  10. Arguments.arguments("gooD", "Good"));
  11. }

上面的代码很容易理解:静态方法testCapitalize()返回了一组测试参数,每个参数都包含两个String,正好作为测试方法的两个参数传入。

如果静态方法和测试方法的名称不同,@MethodSource也允许指定方法名。但使用默认同名方法最方便。

另一种传入测试参数的方法是使用@CsvSource,它的每一个字符串表示一行,一行包含的若干参数用,分隔,因此,上述测试又可以改写如下:

  1. @ParameterizedTest
  2. @CsvSource({ "abc, Abc", "APPLE, Apple", "gooD, Good" })
  3. void testCapitalize(String input, String result) {
  4. assertEquals(result, StringUtils.capitalize(input));
  5. }

如果有成百上千的测试输入,那么,直接写@CsvSource就很不方便。这个时候,我们可以把测试数据提到一个独立的CSV文件中,然后标注上@CsvFileSource

  1. @ParameterizedTest
  2. @CsvFileSource(resources = { "/test-capitalize.csv" })
  3. void testCapitalizeUsingCsvFile(String input, String result) {
  4. assertEquals(result, StringUtils.capitalize(input));
  5. }

JUnit只在classpath中查找指定的CSV文件,因此,test-capitalize.csv这个文件要放到test目录下,内容如下:

  1. apple, Apple
  2. HELLO, Hello
  3. JUnit, Junit
  4. reSource, Resource

练习

下载练习:参数化测试StringUtils (推荐使用IDE练习插件快速下载)

小结

使用参数化测试,可以提供一组测试数据,对一个测试方法反复测试。

参数既可以在测试代码中写死,也可以通过@CsvFileSource放到外部的CSV文件中。

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加微信群讨论

参数化测试 - 图1参数化测试 - 图2