软件单元测试中的“断言”——从石器时代到黄金时代

众所周知,软件测试,尤其是单元测试中必须用到的一个功能就是“Assert”。有些人可能认为它不仅仅是一个Assert语句,也有很多人在使用它时是无知的。他们认为只要是以Assert开头的方法,他们都会使用。一次偶然的机会谈到这个函数,我觉得有必要梳理一下如何使用,以及我对“断言”的理解。希望能帮助你对此有一个系统的认识,也借此机会说说“断言”发展的心路历程。

首先,我们来介绍一些关于断言的知识。对于有经验的程序员,请移至下面的“断言”进化史部分。

在单元测试中,程序员预测程序运行到某个节点时,需要判断必须满足某些逻辑条件,才能进行下面的某些业务逻辑。如果不满足,程序会“报错”甚至“崩溃”。比如一个程序负责“转账”。在实际开始转账操作之前,需要“断言”这个账户是一个“合法”的账户,例如,账户不为空。当有事情发生时,程序开发者可以第一时间知道问题所在,并进行调试,而不是等到交付给用户后才发现问题。事实上,这个功能是TDD(测试驱动开发)的基石之一。

起初,一些单元测试框架(如JUnit)提供了断言语句,以确保程序中某处的逻辑关系必须返回true。如果不为真,则单元测试失败。下面是一个例子。如果程序运行到这一行时返回false,程序将抛出一个错误(如下图1所示)并停止运行。开发人员可以检查为什么会出现这个问题。非常简单粗糙。

除了简单之外,上面的断言还有一个问题,就是触发断言时显示的错误消息不是很友好。如上图1所示,我只是知道有点不对劲,但是没有太多有用的信息。比如最好能显示X和Y的值,这样我就能更快的明白为什么会出问题。后来出现了支持断言的单元测试框架升级版,提供了一系列高级的“断言”语句,增加了一些更友好的程序接口,也提供了更友好的错误消息。例如,下面的示例使用了两个独立的断言语句。

执行的结果如下面的图2所示。你可以看到这个错误结果与上面的“石器时代”相比已经包含了很多有用的信息,比如知道断言失败,显示预期值和实际值。

但是上述方法有一个缺点,就是大量预设的断言方法(比如判断一个方法相等,判断一个方法不相等等等。)来支持各种场景。然后出现了一个新的解决方案,其明星是Hamcrest的框架(其实这个单词是一个叫做angram的文字游戏,就是改变一个原单词中的字母顺序,这个Hamcrest是Matchers的改造)。它由Matcher在assertThat组合上使用。

这有几个优点,

说了这么多,你是不是觉得一个平时经常用到的看似简单的断言,还有很多可以深挖的地方?这只是为了吸引玉石。如果你有任何想法或建议,请使用以下方法。