测试规定行为,而非次要行为

Test for Required Behavior, not Incidental Behavior

测试的一个常见陷阱就是假设你所测试的恰好都是实现的。第一眼看上去,这种观点像是一种美德而非陷阱。然而,换一种说法后,问题就凸显出来了:测试的一种常见陷阱就是将测试硬性地结合到实现的具体细节上,这些细节是附带条件的,与功能预期无关。

当测试被强行关联到次要实现时,为了让实现内容其符合规定行为而作出的修改,可能会导致测试失败,从而导致失败判定。程序员对此的典型反应就是重写测试或重写代码。如果某个失败判定实质上是个正确判定,通常都是害怕、纠结、怀疑共同作用的结果。它具有将附加行为状态升高至必要行为的效果。重写测试时,程序员会重新聚焦测试于必要行为(这是好的),或者粗暴地迁就测试而重新实现(这是不好的)。测试去要足够精确,但也要准确。

例如,Java的String.compareTo或者C的strcmp有三种比较方式,会产生三种必然的结果:如果左边小于右边就是负值,如果左边大于右边就是正值,如果两边相等就会零。这种比较风格运用了大量的API,包括C中的qsort比较器以及Java中的compareTo比较接口。尽管具体的-1和+1常被用在实现里,以表示小于或大于,但程序员经常会错误地假设这些值就相当于确切的需求,因此公开的固化了这些假设来编写测试。

类似的测试问题还会发生在断言空格、精确的单词、文本格式,以及其它附加部分。除非你正在写,比如提供了可配置格式的XML生成器,空格对结果不重要。同样的,在UI控制时将按钮和标签硬性绑定在一起会降低这些附加功能的可修改和可完善性。那么实现中的微调和格式中无关紧要的修改突然就会变成构建的破坏者。

采用白盒测试的单元测试法,过拟合通常也是个问题。白盒测试是运用代码的数据结构来明确测试用例的需求。白盒测试典型的失败模式就是用代码本身的动作来最终断言代码的动作。简单重术已经显而易见的代码是毫无价值的,并且还会导致错误的进度和安全感。

为了高效,测试要做的是“声明合同义务”而非“鹦鹉学舌般再实现一遍”。它们需要丢到黑盒中单元测试,用实际执行情况来描述约定的接口。所以,测试行为要向规定行为看齐。

0%