测试所需行为,而不是附带行为
通常在测试过程中易犯的一个错误是,假定实现方法与你想要测试的完全相同。乍一看这似乎更是美德而不是错误。然而,换种说法问题就更明显了:测试中一个常见的错误是将测试硬绑定到特定的实现方法,而这些实现方法只不过是附带的、与所需的功能无关。
当测试绑定到特定的实现时,对实现做出能兼容所需行为的修改可能导致测试不通过,产生误报。程序员一般是要么重写测试,要么重写代码。假定误报实际上真的是错误,这往往是害怕、不确定和怀疑的结果。它会产生影响让附带行为升成所需行为。在重写测试时,程序员要么重新专注于所需行为的测试(好),要么就简单地硬绑定于新的实现(不好)。测试需要充分精确,但不需要完全精确。
例如,在三态比较中,比如C语言的strcmp或者Java的String.compareTo,结果的需求是如果左边比右边的小则返回负数,左边比右边大则返回正数,相等则返回0。这种比较风格在很多API中广泛使用,包括C语言的QSORT函数和Java的Comparable接口中的compareTo函数。尽管特值-1和+1一般是结果的“负数”和“正数”的特定实现,程序员通常错误地假定这些引起分别代表了实际需求,因而编写了带有这种假定的测试。
测试中类似的问题还出现在断言空白、精确措词或者其它附带的文本格式及表现方面。除非你是在写例如提供可配置格式的xml生成器的时候,空白就不应该对结果起重要作用。类似地,手写按钮和标签等UI控制会减少将来修改和优化这些附带行为的机会;实现中的小的修改和格式中细微的调整也会打破构建。
过于特定的测试通常是使用白盒方法的单元测试中的问题。白盒测试使用代码的结构决定所需的测试用例。白盒测试的典型的失败模式是断言代码做了它做的。简单地重述代码中已经显而易见的东西不会增加任何价值,并会导致对过程和安全性的错误认识。
要更有效率,测试需要表述的是约定的需求而不是重述实现方法。它们需要对要测试的单元采用黑盒的视角,从执行表格中提取出接口。因而,将测试的行为专注于所需的的行为。
原文:Test for Required Behavior, not Incidental Behavior by Kevlin Henney