为什么要进行单元测试?(该段装载于知乎)
- 提高代码质量。代码测试都是为了帮助开发人员发现问题从而解决问题,提高代码质量。
- 尽早发现问题。问题越早发现,解决的难度和成本就越低。
- 保证重构正确性。随着功能的增加,重构(修改老代码)几乎是无法避免的。很多时候我们不敢重构的原因,就是担心其它模块因为依赖它而不工作。有了单元测试,只要在改完代码后运行一下单测就知道改动对整个系统的影响了,从而可以让我们放心的重构代码。
- 简化调试过程。单元测试让我们可以轻松地知道是哪一部分代码出了问题。
- 简化集成过程。由于各个单元已经被测试,在集成过程中进行的后续测试会更加容易。
- 优化代码设计。编写测试用例会迫使开发人员仔细思考代码的设计和必须完成的工作,有利于开发人员加深对代码功能的理解,从而形成更合理的设计和结构。
- 单元测试是最好的文档。单元测试覆盖了接口的所有使用方法,是最好的示例代码。而真正的文档包括注释很有可能和代码不同步,并且看不懂。
规范?
-
文件以_test.go为后缀的源代码文件都是测试代码部分,如果使用go build编译源代码时不会包含进该测试文件。
-
源代码中的函数名首字母要大写
-
单元测试函数名前缀 TestXxx,Xxx使用首字母大写,具体名字任意
-
单元测试函数代码中要传递的参数:func TestAdd(t *testing.T) {……}
-
使用go test命令运行测试
栗子?
首先创建文件add.go
用于写正常的逻辑代码,然后再创建文件add_test.go
用于写单元测试代码,最后在该目录下使用命令go mod init 包名
命令指定当前包名,我的包名是add,该包名任意,只要在代码中指定包名时对应就行,该命令会自动在该目录下创建go.mod
文件
add.go文件写正常的逻辑代码,这里我写了两个函数,它们都是求和
package add
// import "fmt"
func Add(a, b int) (result int) {
result = a + b
return result
}
func Sum(a, b int) (result int) {
result = a + b
return result
}
// func other() {
// fmt.Println(Add(5, 8))
// }
add_test.go文件写单元测试代码
package add
import (
"fmt"
"testing"
)
func TestSdd(t *testing.T) { // 测试源代码中Add函数,这里我故意不写TestAdd而使用TestSdd为了说明后面哪个名字任意,但最好改为Add,望文生义
result := Add(3, 8) // 执行源代码中的Add函数
want := 11 // 定义一个期望值
if result == want { // 判断结果是否正确
fmt.Println("执行通过")
} else {
t.Errorf("execepted:%v, rest%v", want, result) // 打印错误信息
}
}
func TestSum(t *testing.T) {
result := Sum(10, 15)
want := 23 // 我这里故意写了个错误的期望值
if result == want {
fmt.Println("执行通过")
} else {
t.Errorf("execepted:%v, rest%v", want, result)
}
}
运行测试命令go test -v
,。加参数-v
,能打印更加详细的信息。可以看到信息,运行TestBdd函数通过,运行TestSum失败,所以我们就能定位到TestSum函数逻辑实现失败
>>>>>>>>>>>>>> go test -v
=== RUN TestSdd
执行通过
--- PASS: TestBdd (0.00s)
=== RUN TestSum
add_test.go:31: execepted:23, rest25
--- FAIL: TestSum (0.00s)
FAIL
exit status 1
FAIL add 0.185s
其它常用参数?
如果在同一个包下有很多测试文件,使用go test
命令后,所有的测试代码都会运行,但我们只想运行一个测试代码,可以添加-run="正则表达式"
,只有匹配到正则表达式的函数名才运行。可以看到TestSdd运行了,而TestSum没有运行
>>>>>>>>>>>>>> go test -v -run="dd"
=== RUN TestSdd
执行通过
--- PASS: TestSdd (0.00s)
PASS
ok add 0.254s