引入
谷歌一搜Go语言如何写单元测试,千篇一律都是关于整个包只有一个函数怎么实现测试。然而,很多情况下无法对复杂结构的代码进行测试。这篇博文教你在函数依赖于结构体的条件下实现单元测试。
最简单的例子
hello.go文件
package main
import (
"fmt"
)
func hello() string {
return "Hello, Testing!"
}
func main() {
fmt.Println(hello())
}
hello_test.go文件
package main
import (
"testing"
)
func TestHello(t *testing.T) {
expectedStr := "Hello, Testing!"
result := hello()
if result != expectedStr {
t.Fatalf("Expected %s, got %s", expectedStr, result)
}
}
hello.go文件与hello_test.go文件都是在main包下的,区别在于,单元测试文件要以XXX_test.go结尾。
这里我们要测试的是helle()函数的功能,其输入为空,理想输出是"Hello, Testing!"字符串。在测试文件中,首先用result保存hello()函数的结果,接着对照这个实际结果与理想结果是否相同。注意!!!错误输出函数t.Fatalf最好保留,不要换成t.error,不然很大可能编译不出。
重点来了,进阶内容
下面我要展示一个复杂的单元测试过程,代码很长。如果你没有耐心仔细看,你可以跟随我的文字捋清楚思路。
首先需要知道的前提,这个单元测试需要完成的是job.go文件当中run()函数的测试,以及Suspend()和Resume()的测试。run()函数的测试对应这些job_test.go文件当中的estPollerJobRunLog()函数,Suspend()和Resume()的测试对应这些job_test.go文件当中的TestPollerJobSuspendResume()函数。
这些需要被测试的函数,都是依赖于一个结构体的,单独根本无法测试,那怎么办呢?
需要什么我们就给什么,先创建这个结构体实例,再用这个实例调用测试方法,比较实际结果和理想结果即可。
当然,如果想透彻的理解单元测试,就多花点时间仔细分析下面的代码结构。
job.go文件
package main
import (
"fmt"
"log"
"net/http"
"time"
)
type Logger interface {
Log(...interface{
})
}
type SuspendResumer interface {
Suspend() error
Resume() error
}
type Job interface {
Logger
SuspendResumer
Run() error
}
type ServerPoller interface {
PollServer() (string, error)
}
type PollerLogger struct{
}
type URLServerPoller struct {
resourceUrl string
}
type PollSuspendResumer struct {
SuspendCh chan bool
ResumeCh chan bool
}
type PollerJob struct {
WaitDuration time.Duration
ServerPoller
Logger
*PollSuspendResumer
}
func NewPollerJob(resourceUrl string, waitDuration time.Duration) PollerJob {
return PollerJob{
WaitDuration: waitDuration,
Logger: &PollerLogger{
},
ServerPoller: &URLServerPoller{
resourceUrl