【CS61B】3. Testing

本章我们将探索如何编写自己的测试

临时测试

大概就是自己手动编一个测试, 把原始输入和预期输入都预设好, 然后自己遍历比较正确性
没啥可说的, 不是重点

JUnit 测试

org.junit 库提供了许多有用的方法和有用的功能来简化测试的编写

1
2
3
4
5
6
public static void testSort() {
    String[] input = {"i", "have", "an", "egg"};
    String[] expected = {"an", "egg", "have", "i"};
    Sort.sort(input);
    org.junit.Assert.assertArrayEquals(expected, input);
}

在一个无功能的Sort.sort方法上运行testSort()会得到报错:

1
2
3
4
Exception in thread "main" arrays first differed at element [0]; expected:<[an]> but was:<[i]>
    at org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:55)
    at org.junit.Assert.internalArrayEquals(Assert.java:532)
    ...

选择排序

这是最简单的排序算法, 包括以下三个步骤:

  1. 找到最小的元素
  2. 将其移至最前
    1. 最简单的实现方法是将其与首位元素(还未排序的)进行交换
  3. 对剩余的 N-1 项进行排序

寻找最小值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**  Returns the smallest string in x. */
public static String findSmallest(String[] x) {
    String smallest = x[0];
    for (int i = 0; i < x.length; i += 1) {
        if (x[i] < smallest) {
            smallest = x[i];
        }
    }
    return smallest;
}

比较字符串

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/** Returns the smallest string in x. 
  * @source Got help with string compares from https://goo.gl/a7yBU5. */
public static String findSmallest(String[] x) {
    String smallest = x[0];
    for (int i = 0; i < x.length; i += 1) {
        int cmp = x[i].compareTo(smallest);
        if (cmp < 0) {
            smallest = x[i];
        }
    }
    return smallest;
}
  • 如果 str1 < str2,则 str1.compareTo(str2) 方法将返回负数
  • 如果它们相等,则返回 0
  • 如果 str1 > str2,则返回正数

交换

1
2
3
4
5
public static void swap(String[] x, int a, int b) {
    String temp = x[a];
    x[a] = x[b];
    x[b] = temp;
}

这里需要用一个 temp 变量作为中间临时变量来存储值, 辅助交换
注意这里 temp 的类型应与交换的二者类型相同

修改findsmallest

这里我们实现的 swap 函数接收的参数是交换的索引, 而非数值
因此我们需要修改 findsmallest 函数的返回值, 使其和 swap 适配

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static int findSmallest(String[] x) {
    int smallestIndex = 0;
    for (int i = 0; i < x.length; i += 1) {
        int cmp = x[i].compareTo(x[smallestIndex]);
        if (cmp < 0) {
            smallestIndex = i;
        }
    }
    return smallestIndex;
}

递归辅助方法

现在我们已经实现了对一个元素的排序, 接下来可以通过递归的方法, 将其推广, 最终完成对一整个序列的排序
然而, Java 中没有类似于 Python 中"切片"的方法, 无法抽取数列的一部分
我们考虑引入一个私有辅助 sort 函数, 引入一个 start 参数, 表示开始位置

1
2
3
4
5
6
/** Sorts strings destructively starting from item start. */
private static void sort(String[] x, int start) { 
   int smallestIndex = findSmallest(x);
   swap(x, start, smallestIndex);
   sort(x, start + 1);
}

然后, 在 public 函数中调用 private 函数, start 设为 0, 即从头开始排序

1
2
3
4
/** Sorts strings destructively. */
public static void sort(String[] x) { 
   sort(x, 0);
}

调试

前面改了很多功能, 导致一些 bug
不多赘述了, 大概就是要注意函数之间的衔接:
返回值<->传入参数
需要相互匹配兼容
同时, 也需要同步地修改 test 中的函数调用

更好的 JUnit

现在的 JUnit 使用方式有些冗余, 我们尝试用一些方法将其简化并变得更美观

测试注释

感觉跟 python 的修饰器功能似的

  • 每个方法前面加上 @org.junit.Test(无分号)
  • 将每个测试方法更改为非静态
  • 从 TestSort 类中删除我们的 main 方法

完成这三件事后,如果我们使用 Run->Run 命令在 JUnit 中重新运行代码,则所有测试都将执行,而无需手动调用

import语句

这可以让我们调用的库中的函数更加简洁

  • 将 import 语句 import org.junit.Test;

  • 可以用简单的 @Test 替换 @org.junit.Test 的所有实例

  • 添加第二个 import 语句 import static org.junit.Assert.*

  • 可以省略任何我们有 org.junit.Assert 的任何地方

    • 例如,我们可以简单地 assertEquals(expected2, actual2); 将 org.junit.Assert.assertEquals(expected2, actual2);
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计