0%

测试行为而不是实现

你的Calculator类是你最受欢迎的开源项目之一,拥有大量的用户。

1
2
3
4
5
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}

你也对该类做了测试,确保其正确工作。
1
2
3
4
5
public void testAdd() {
assertEquals(3, calculator.add(2, 1));
assertEquals(2, calculator.add(2, 0));
assertEquals(1, calculator.add(2, -1));
}

然而,一个很棒的库承诺只要你用使用它取代当前的add操作符,就可以给你的代码提供几倍的提效。你修改了你的代码以使用该类库:
1
2
3
4
5
6
7
8
9
public class Calculator {
private AdderFactory adderFactory;
public Calculator(AdderFactor adderFactory) { this.adderFactory = adderFactory; }
public int add(int a, int b) {
Adder adder = adderFactory.createAdder();
ReturnValue returnValue = adder.compute(new Number(a), new Number(b));
return returnValue.convertToInteger();
}
}

这是很简单的事,但是代码的测试用例怎么处理呢?由于只修改了代码的实现而用户可见行为没变,应该是没有测试用例需要修改才对。大多数情况下,测试用例专注于测试代码的public API,且代码的实现细节无需暴露给测试用例

测试与具体实现无关将使得其更易维护,因为在每次你想对实现做修改时,测试用例都无需改变。因为测试用例可以作为范例来展示你的类方法如何被使用,因此public APIs也会更容易被理解,因此即使对于实现不了解的人也可以通过阅读测试用例而明确该类该如何被使用。

也会有很多场景是需要对实现做测试的(例如,你要保证你的实现是从cache读取数据而不是从数据读取),但这应该是更不常见的场景因为在大多数情况下,测试用例是独立于实现的。

对了,实现变化时测试用例的准备可能会发生变化(例如,如果你修改了类在TA的构造器中引入一个依赖对象,那么在测试用例中构造该类时时就需要创建传入这些依赖),但只要面向用户的行为未发生改变,那么真实的测试用例内容不应需要改变。

高光评论

  • This could also be extended to UI level tests (Selenium, mobile), where UI element changes should not affect tests unless the user/system flow changes (drastically).

参考文献

https://testing.googleblog.com/2013/08/testing-on-toilet-test-behavior-not.html