返回

拷问你的单元测试

前端

对于每名测试人员来说,自动化测试的目的是通过优秀清晰的错误报告来快速查出故障,然而,很少有人会花时间去思考一个好的错误报告需要哪些信息。在这个问题之前,我们已经详细地讨论过每个单元测试必须回答的5个问题,所以这次我们将它们一笔带过。现在,绝大多数测试框架允许你跳过其中的一些问题,甚至所有问题,事实上,这是非常不负责任的。

当然,跳过这些问题并不会让你的代码 перестать работать - 不能工作,它只是让你的单元测试功能大大折扣。没有合理的报告,你的测试用例将没有意义。甚至更糟,每个错误报告只展示问题的表面,而不能准确反映出问题的本质。大多数时候,使用这些错误报告追踪错误都困难重重,开发者不知道哪里出了问题,不知道问题有多严重。

为了撰写优秀易读的错误报告,最重要的一个点是明确测试用例的目的是什么。测试用例必须回答一系列问题,问题的数量和具体内容取决于具体的需求,但是以下五个基本问题必须回答:

  • 当前测试要解决的问题是什么?
  • 我尝试对这个错误作什么操作?
  • 我希望得到什么结果?
  • 我的测试框架得到了什么结果?
  • 接下来我该如何操作?

基本上,这是测试用例最基本的问题,针对这些问题,我们可以进一步细分出一些小问题。

  • 我在测试某个特定模块吗?这个模块是做什么的?
  • 出现错误时,我该怎么办?
  • 出现错误时,我该如何调试?

回答这些问题可以帮助你缩小错误的范围,让你更加快速地定位出出错代码的位置。

一般情况下,测试框架通过内置断言来回答这些问题,但是遗憾的是,断言常常是语言级的,或者更糟糕,库级的,很少有断言能够回答出测试用例提出的问题。通常来说,内置断言只能回答最后两个问题,即:

  • 我希望得到什么结果?
  • 我的测试框架得到了什么结果?

但只有回答出所有问题,测试用例的价值才能得到充分体现。幸运的是,解决这个问题的方法很简单,就是编写自己的断言。

编写断言的过程也不复杂,甚至并不需要一个专门的测试框架。只需要创建一个类,并在其中包含一些特定语言的工具方法即可。下面是Go中一个简单的断言工具包:

package assert

import (
    "fmt"
    "runtime"
)

// Equal 返回两个值是否相等,若不相等,使用错误信息提示。
func Equal(expected, actual interface{}, msg string) {
    if expected != actual {
        _, file, line, _ := runtime.Caller(1)
        fmt.Printf("\033[31m%s:%d: 断言失败:\n\t期望:%#v\n\t实际:%#v\n\n", file, line, expected, actual)
        if msg != "" {
            fmt.Printf("\t原因:%s\n", msg)
        }
    }
}

使用这个简单的类,就可以编写出优秀的测试用例了,如下所示:

package main

import (
    "testing"

    "github.com/liminggo/assert"
)

func TestDivide(t *testing.T) {
    assert.Equal(1, divide(2, 2), "")
    assert.Equal(2, divide(10, 5), "")
    assert.Equal(0, divide(10, 0), "被除数不能为0")
}

func divide(x, y int) int {
    if y == 0 {
        return 0
    }
    return x / y
}

使用这个类,你就不需要再去纠结错误报告是否好读的问题了,它不会让你失望的。