7.2 Lambda表达式的单元测试

7.2 Lambda表达式的单元测试 - 图1单元测试是测试一段代码的行为是否符合预期的方式。

通常,在编写单元测试时,怎么在应用中调用该方法,就怎么在测试中调用。给定一些输入或测试替身,调用这些方法,然后验证结果是否和预期的行为一致。

Lambda表达式给单元测试带来了一些麻烦,Lambda表达式没有名字,无法直接在测试代码中调用。

你可以在测试代码中复制Lambda表达式来测试,但这种方式的副作用是测试的不是真正的实现。假设你修改了实现代码,测试仍然通过,而实现可能早已在做另一件事了。

解决该问题有两种方式。第一种是将Lambda表达式放入一个方法测试,这种方式要测那个方法,而不是Lambda表达式本身。例7-8是一个将一组字符串转换成大写的方法。

例7-8 将字符串转换为大写形式

  1. public static List<String> allToUpperCase(List<String> words) {
  2. return words.stream()
  3. .map(string -> string.toUpperCase())
  4. .collect(Collectors.<String>toList());
  5. }

在这段代码中,Lambda表达式唯一的作用就是调用一个Java方法。将该Lambda表达式单独测试是不值得的,它的行为太简单了。

如果换我来测试这段代码,我会将重点放在方法的行为上。比如例7-9测试了流中有多个单词的情况,它们都被转换成对应的大写。

例7-9 测试大写转换

  1. @Test
  2. public void multipleWordsToUppercase() {
  3. List<String> input = Arrays.asList("a", "b", "hello");
  4. List<String> result = Testing.allToUpperCase(input);
  5. assertEquals(asList("A", "B", "HELLO"), result);
  6. }

有时候Lambda表达式实现了复杂的功能,它可能包含多个边界情况、使用了多个属性来计算一个非常重要的值。你非常想测试该段代码的行为,但它是一个Lambda表达式,无法引用。

作为例子,让我们来看一个比大写转换更复杂一点的方法。我们要把字符串的第一个字母转换成大写,其他部分保持不变。使用流和Lambda表达式,编写的代码形如例7-10所示。在➊处使用Lambda表达式做转换。

例7-10 将列表中元素的第一个字母转换成大写

  1. public static List<String> elementFirstToUpperCaseLambdas(List<String> words) {
  2. return words.stream()
  3. .map(value -> {
  4. char firstChar = Character.toUpperCase(value.charAt(0));
  5. return firstChar + value.substring(1);
  6. })
  7. .collect(Collectors.<String>toList());
  8. }

如果要测试这段代码,我们必须创建一个列表,然后将想要测试的各种情况都测试到。例7-11展示了这种方式有多么繁琐,别担心,我们有办法!

例7-11 测试字符串包含两个字符的情况,第一个字母被转换为大写

  1. @Test
  2. public void twoLetterStringConvertedToUppercaseLambdas() {
  3. List<String> input = Arrays.asList("ab");
  4. List<String> result = Testing.elementFirstToUpperCaseLambdas(input);
  5. assertEquals(asList("Ab"), result);
  6. }

别用Lambda表达式。我知道,在一本介绍如何使用Lambda表达式的书里,这个建议有点奇怪,但是方楔子钉不进圆孔。既然如此,大家一定会问如何测试代码,同时享有Lambda表达式带来的便利?

请用方法引用。任何Lambda表达式都能被改写为普通方法,然后使用方法引用直接引用。

例7-12将Lambda表达式重构为一个方法,然后在主程序中使用,主程序负责转换字符串。

例7-12 将首字母转换为大写,应用到所有列表元素

  1. public static List<String> elementFirstToUppercase(List<String> words) {
  2. return words.stream()
  3. .map(Testing::firstToUppercase)
  4. .collect(Collectors.<String>toList());
  5. }
  6. public static String firstToUppercase(String value) {
  7. char firstChar = Character.toUpperCase(value.charAt(0));
  8. return firstChar + value.substring(1);
  9. }

把处理字符串的的逻辑抽取成一个方法后,就可以测试该方法,把所有的边界情况都覆盖到。新的测试用例如例7-13所示。

例7-13 测试单独的方法

  1. @Test
  2. public void twoLetterStringConvertedToUppercase() {
  3. String input = "ab";
  4. String result = Testing.firstToUppercase(input);
  5. assertEquals("Ab", result);
  6. }