本章我们将探索如何编写自己的测试
临时测试
大概就是自己手动编一个测试, 把原始输入和预期输入都预设好, 然后自己遍历比较正确性
没啥可说的, 不是重点
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)
...
|
选择排序
这是最简单的排序算法, 包括以下三个步骤:
- 找到最小的元素
- 将其移至最前
- 最简单的实现方法是将其与首位元素(还未排序的)进行交换
- 对剩余的 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);