我曾经花费了无数时间来思考到底该如何编写单元测试。
我去搜索了Go语言的单元测试模式
很多人通过外部依赖来使用断言。事实上,我认为类似 isNil(v interface{}) bool
这样的范型函数最开始确实能够提升开发速度,但是长远来看,我想如果 接受Go的强类型天性 而不是只想着回避它,会更加有意义。编写地道的代码对于提升质量和理解代码都有好处。
然后,我去向Go的核心库寻求答案
当我明白我需要从哪寻找答案的时候,我注意到了一个地方。
Go的核心库里面有一个专门为了测试目的的包(package): [net/http/httptest][2]
。这个包一定会有好的测试方式。
我找到了什么
Brad Fitzpatrick的代码,还能有什么。
这里是一个稍微修改过的 recorder_test.go
的版本:
这是怎么一回事?
这个测试函数包含三个部分。
第一部分(2–28行):匹配器(matcher)
代码第一行定义了一个函数类型: checkFunc
。这个函数签名有一个参数接收我们需要测试的数据。 checkFunc
的参数应该包括目标函数的所有返回值。在这里,我们测试一个ResponseRecorder包含的方法,需要测试的状态都在ResponseRecorder里面,它作为 checkFunc
的唯一一个参数。
匹配器函数是闭包: 包含了期待值。如果期待值与实际结果不匹配则 checkFunc
会返回错误。
第二部分(30–60行):测试用例
使用一个匿名的 结构体(struct)
存放测试数据。所有的测试都被定义为:
- 对用例的描述
- 输入
- 一个
checkFunc
的切片用来承载期望值
第三部分(62–74行):测试逻辑
这是我们要亲自去做的部分,使用测试用例的数据来 checkFunc
切片:如果有错误返回,就直接跳到 t.Error()
。
TDD还是BDD,你自己决定
如果更喜欢 表格驱动测试(table-driven tests) ,那每一个测试里面可能要使用不同值重复定义匹配器,用用例的名称来描述目标函数的测试场景。
对于偏向 BDD 的开发人员,测试用例的名称会描述作为输出的期望值,而且并不是每个匹配器都会被每一个用例使用。
或者如果你像我这样 不希望命令行界面被测试淹没 的话,可以自行按需搭配使用!