美文网首页Java学习笔记测试员的那点事
浅谈软件单元测试中的“断言” (assert) -- 从石器时代

浅谈软件单元测试中的“断言” (assert) -- 从石器时代

作者: CloudsDocker | 来源:发表于2016-09-24 13:46 被阅读1445次

大家都知道,在软件测试特别是在单元测试时,必用的一个功能就是“断言”(Assert),可能有些人觉得不就一个Assert语句,没啥花头,也有很多人用起来也是懵懵懂懂,认为只要是Assert开头的方法,拿过来就用。一个偶然的机会跟人聊到此功能,觉得还是有必要在此整理一下如何使用以及对“断言”的理解。希望可以帮助大家对此有一个系统的理解,也趁机聊聊“断言”发展一路过来的心路历程。

基础知识

首先稍微介绍一下断言相关知识,对于有经验的程序员请移步到下面的“断言”进化史部分。

什么是断言

在单元测试时,程序员预计在程序运行到某个节点位置,需要判断某些逻辑条件必须满足,这样下面的一些业务逻辑才可以进行下去,如果不满足,程序就会"报错"甚至是"崩溃"。比如说,一段程序是负责“转账”,在真正开始转账操作前首先需要“断言”这个账户是一个“合法”的账户,比如账户不是null。当出现些状况时,程序开发人员就可以在第一时间知道这个问题,可以去debug除错,而非等到交付给用户后才发现问题。其实这个功能是TDD (Test Driven Develop)的基石之一。

“断言” vs "异常"或者错误, 即 Assert vs. Exception/Error

  • “断言”通常是给程序开发人员自己使用,并且在开发测试期间使用。而异常等在程序运行期间触发
  • 通常“断言”触发后程序“崩溃”退出,不需要从错误中恢复。而“异常”通常会使用try/catch等结构从错误中恢复并继续运行程序。

“断言”进化史

“石器时代”

一开始的一些单元测试框架(比如JUnit)提供的断言语句,这样在程序某个地方确保某个逻辑关系肯定返回是true,如果不是true,这个单元测试就是没有测试通过。如下就是一个例子,如果程序运行到此行时返回false程序就会抛出一个错误(如下图一)并停止运行,开发人员可以去检查下为什么出现此问题。非常的简单粗爆。

assert(x=y);
blog_assert_1.png

“青铜时代”

上面这种断言除了简单之外,是有一个问题,就是当断言被触发时显示出来的错误消息不是很友好。如上图一,只是知道出错了,但是并没有太多有用的信息,比如最好是能显示出x与y的值来,这样好更快的理解为啥出错。后来,支持断言的单元测试框架升级版本出现了,它们提供了一系列的高级”断言“语句,添加了一些更加友好的程序接口,同时还提供比较亲民的错误消息,比如下面的例子使用了两个单独的断言语句。

int x=111;
int y=222;      
assertEquals(x, y);
assertNotEquals(x, y);

执行的结果如下图二,你可以看到这个错误结果相对于上面“石器时代”已经包括了不少有用的信息,比如除了知道断言失败外还显示了期望的值以及实际值

blog_assert_2.jpg

“黄金时代”

但是上面这种方式有一个弊端,就是需要大量的预置断言方法(比如判断相等一个方法,判断不相等一个方法等),去支持各种场景。接下来又出现了新的解决方案,其中的明星就是Hamcrest (其实这个词是使用一种叫做angram的文字游戏,即把一个原来单词中的字母顺序改变,这个Hamcrest就是从Matchers的变形)框架。是使用一种assertThat组合上Matcher来使用。

这个有多个好处,

  • 首先是支持了在Java8中才迟迟引入的流式编程(Stream),即每个Matcher执行完后会再返回一个Matcher,这样可以一个套一个组成一个Matcher链
  • 另外Hamcrest还使用了非常接近于人类自然语言以及使用and/or/not等逻辑判断的方式来写测试方法,比如当你看到下面的测试语句肯定会一目了然:
assertThat(actual, is(not(equalTo(expected)));
  • 还有一个好处是输出的断言消息更加易读。
  • 另外还有一个好处即Hamcrest框架支持泛型TypeSafe,即在编译时就会找到类型不匹配的错误。比如下面第一个是传统的断言,在编译期不会报错,但是运行时会失败,而第二个会在编译时报错,就不用等到运行期。
assertEquals("abc", 123); // 1
assertThat(123, is("abc")); // 2
blog_assert_3.jpg
  • 使用Hamcrest的最后一个好处是对测试框架的“解耦合”,即,使用此框架你可以现在使用Junit后面可以转到TestNG。甚至你自己去扩展自己实现。

总结

上面说了这么多,是不是感觉平时经常使用的一个看似简简单单的Assert还有不少的东西可以深挖一下滴。这个只是抛砖引玉,如果大家还有什么点子或建议请使用下面的方式。

联系我:

Reference:

相关文章

  • 浅谈软件单元测试中的“断言” (assert) -- 从石器时代

    大家都知道,在软件测试特别是在单元测试时,必用的一个功能就是“断言”(Assert),可能有些人觉得不就一个Ass...

  • 自定义断言的实现方法

    断言定义 熟悉单元测试的同学都知道在测试用例中,执行完测试用例后,通过断言(assert)判断测试结果是pass还...

  • python unittest之断言及示例

    前言 python unintest单元测试框架提供了一整套内置的断言方法。 如果断言失败,则抛出一个Assert...

  • if与assert的区别

    assert 断言(assert)的语义: if 语义: 区别 1.assert语句仅仅在debug版本中才有效,...

  • 断言NSAssert

    NSAssert与assert NSAssert和assert是断言,主要的差别是assert在断言失败的时候只是...

  • NSAssert断言

    NSAssert与assert NSAssert和assert是断言,主要的差别是assert在断言失败的时候只是...

  • 函数的使用

    Lua中,assert的作用Assert(断言)编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假...

  • Python的异常处理(三)

    assert 从上面的举例中可以基本了解了 assert 的特点。assert,翻译过来是“断言”之意。asser...

  • TestNG断言

    TestNG中的Assertion,也是断言。断言是测试中最难写的部分。 Assert类(硬断言) 断言类是Ass...

  • maven tests

    (一)关于断言assert 断言assert与异常exception的区别在于:1、断言用于调试代码2、异常用于捕...

网友评论

    本文标题:浅谈软件单元测试中的“断言” (assert) -- 从石器时代

    本文链接:https://www.haomeiwen.com/subject/wbexyttx.html