0%

测试状态 V.S. 测试调用

单元测试主要有两种方式来验证待验证代码是否正常工作:通过测试状态和通过测试调用。二者有什么分别呢?

测试状态意味着只去验证待测代码返回的结果是否正确

1
2
3
4
5
6
7
8
public void testSortNumbers() {
NumberSorter numberSorter = new NumberSorter(quicksort, bubbleSort);
// Verify that the returned list is sorted. It doesn't matter which sorting
// algorithm is used, as long as the right result is returned.
assertEquals(
new ArrayList(1, 2, 3),
numberSorter.sortNumbers(new ArrayList(3, 1, 2)));
}

测试调用意味着要验证待测代码确实正确地调用了某些方法

1
2
3
4
5
6
7
8
9
public void testSortNumbers_quicksortIsUsed() {
// Pass in mocks to the class and call the method under test.
NumberSorter numberSorter = new NumberSorter(mockQuicksort, mockBubbleSort);
numberSorter.sortNumbers(new ArrayList(3, 1, 2));
// Verify that numberSorter.sortNumbers() used quicksort. The test should
// fail if mockQuicksort.sort() is never called or if it's called with the
// wrong arguments (e.g. if mockBubbleSort is used to sort the numbers).
verify(mockQuicksort).sort(new ArrayList(3, 1, 2));
}

第二种测试(测试调用)可能会使得测试覆盖率比较高,但TA并不能告诉你待测的排序算法是否正常工作,只能知道quicksort.sort确实被调用了。这也是为什么在大多数场景下,都想要测试状态而不是测调用。
总的来说,调用在正确性不止依赖于返回值时才需要测试。在上述的例子中,只有在“quicksort是否被调用真的很重要时(比如,如果用了其他排序算法会导致方法执行很慢)”,才应该去测试调用,否则测试调用就真的没有必要。

一些其他可能会需要测试调用的场景

  • 待测代码调用了一个方法,该方法在不同的调用顺序及调用次数下,表现完全不同,例如有副作用(例如,希望保证只会发送一封邮件),延迟(例如,想要确保只会产生确定次数的磁盘访问)或者多线程问题(比如,如果以错误的顺序访问,会导致产生死锁)。测试调用保证如果这些方法没有被正确调用,测试用例就挂。
  • 在测试UI,且UI的渲染细节从UI逻辑中抽象分离出去了(例如使用MVC或MVP)。在测试Controller/Presenter时,只会关心View的具体方法确实被调用了,而不是去关心真的渲染成了什么样,因此你可以测与View之间的调用。类似的,当测试View时,你可以测与Controller/Presenter之间的调用。

文章翻译自:

https://testing.googleblog.com/2013/03/testing-on-toilet-testing-state-vs.html