测试驱动开发是保持高代码质量的好方法,同时保护本身免于回归,并向本身和其余人证实本身的代码完成了预期的工做。 这里有五个技巧和窍门能够改善你的测试。函数
Go坚持同一个文件夹中的文件属于同一个包,除了_test.go
文件。将测试代码移出软件包,可让您编写测试,就好像您是软件包的真正用户。你不能摆弄内部,而是专一于暴露的界面,并老是想着你可能会添加到你的API的任何噪音。
这样可让你自由地更改内部,而无需调整测试代码。单元测试
若是你确实须要对一些内部进行单元测试,用_internal_test.go做为后缀建立另外一个文件。内部测试必然比接口测试更脆弱, 可是它们是确保内部组件行为的一个很好的方法,并且若是你使用测试驱动开发的话,它是特别有用的。测试
去创建和运行速度很是快,因此没有任何理由能够说为何不每次保存时都运行整个测试case。ui
当你在这个时候,为何不去同时运行go vet,golint和goimports呢?
在Sublime Text中,这能够经过安装GoSublime并在添加如下配置项以前按下 Cmd +.,5来实现:code
“on_save”: [{ “cmd”: “gs9o_run_many”, “args”: { “commands”:[ [“clear”], [“sh”, “if [ -f onsave.sh ]; then ./onsave.sh; else gofmt -s -w ./ && go build . errors && go test -i && go test && go vet && golint ; fi”] ], “focus_view”: false } }], “fmt_cmd”: [“goimports”]
上面的脚本首先检查项目是否有一个onsave.sh
文件,它会运行。这使您能够轻松关闭不适用的软件包的自动测试功能。对象
匿名结构和复合文字容许咱们写出很是清晰和简单的表格测试,而不依赖任何外部包。 下面的代码容许咱们为一个还没有写成的“Fib”函数设置一系列的测试:blog
var fibTests = [] struct { n int // input expected int //预期结果 } { {1,1}, {2,1}, {3,2}, {4,3}, {5,5}, {6,8}, {7,13}, }
而后,咱们的测试函数只是覆盖在切片上,在声明结果正确以前为每一个“n”调用“Fib”方法:接口
func TestFib(t *testing.T) { for _, tt := range fibTests { actual := Fib(tt.n) if actual != tt.expected { t.Errorf("Fib(%d): expected %d, actual %d", tt.n, tt.expected, actual) } } }
看看你是否能够本身写“Fib”函数来让测试经过,或者你能够从Dave Chaney那里找到解决方法。ip
若是你须要模拟你的代码依赖的东西来正确地测试它,那么极可能是一个接口的好选择。即便你依赖于一个你不能改变的外部包,你的代码仍然可使用一个外部类型将会知足的接口。ci
通过几年的写mocks接口,我终于找到了模拟接口的完美方式,而不须要添加任何依赖项到咱们的项目中: [check out Moq](https://medium.com/@matryer/meet-moq-easily-mock-interfaces-in-go-476444187d10)。
假设咱们正在导入这个外部包:
package mailman import “net/mail” type MailMan struct{} func (m *MailMan) Send(subject, body string, to ...*mail.Address) { // some code } func New() *MailMan { return &MailMan{} }
若是咱们正在测试的代码须要一个MailMan
对象,咱们的测试代码能够调用它的惟一方法是提供一个实际的MailMan
实例。
func SendWelcomeEmail(m * MailMan,to ... * mail.Address){...}
这意味着每当咱们运行咱们的测试,一个真正的电子邮件能够发送。想象一下,若是咱们已经实现了上面的保存功能。咱们很快就会惹恼咱们的测试用户,或者拿出一大笔服务费用。 另外一种方法是将这个简单的接口添加到您的代码中:
type EmailSender interface{ Send(subject, body string, to ...*mail.Address) }
固然,MailMan已经知足了这个接口,由于咱们首先从他的Send
方法签名 - 因此咱们仍然能够像之前同样传入MailMan
对象。 但如今咱们能够写一个测试邮件发件人:
type testEmailSender struct{ lastSubject string lastBody string lastTo []*mail.Address } // make sure it satisfies the interface var _ package.EmailSender = (*testEmailSender)(nil) func (t *testEmailSender) Send(subject, body string, to ...*mail.Address) { t.lastSubject = subject t.lastBody = body t.lastTo = to }
如今咱们能够更新咱们的SendWelcomeEmail
函数来取接口,而不是具体的类型:
func SendWelcomeEmail(m EmailSender,to ... * mail.Address){...}
在咱们的测试代码中,咱们能够发送咱们的假发送者,并在调用目标函数以后在这些字段上进行断言:
func TestSendWelcomeEmail(t *testing.T) { sender := &testEmailSender{} SendWelcomeEmail(sender, to1, to2) if sender.lastSubject != "Welcome" { t.Error("Subject line was wrong") } if sender.To[0] != to1 && sender.To[1] != to2 { t.Error("Wrong recipients") } }
若是你想进一步探索mock特性,那么必定要看看check out Moq, 它不只描述了一个写模拟的好方法,并且还为你写。