想象有一个User
包,其中只包含两个简单的方法单元测试时,接收机的方法,叫对方
Hello
它说“你好”Say
它实现了用户如何说话
原创
package user
import "fmt"
type user struct {}
func (u user) Hello() {
u.Say("Hello")
}
func (u user) Say(sentence string) {
fmt.Println(sentence)
}
但是,我们无法单元测试Hello
,因为它取决于Say
这是不可嘲弄的。
在熬过StackOverflow和Goole之后,我总结了两种解决问题的方法,但没有一个是完美的。
方法1 - 使用拉姆达FUNC
user.go
package user
import "fmt"
type user struct{}
func (u user) Hello() {
say("Hello")
}
func (u user) Say(sentence string) {
say(sentence)
}
var say = func(sentence string) {
fmt.Println(sentence)
}
user_test.go
package user
import (
"testing"
)
func TestHello(t *testing.T) {
sayCalled := 0
sayCallArg := ""
mockSay := func(sentence string) {
sayCalled++
sayCallArg = sentence
}
say = mockSay
u := user{}
u.Hello()
if sayCalled != 1 {
t.Fatalf("not called")
}
if sayCallArg != "Hello" {
t.Fatalf("wrong arg")
}
}
方法2 - 使用接口
user.go
package user
import "fmt"
type user struct {
sayer Sayer
}
func (u user) Hello() {
u.sayer.Say("Hello")
}
func (u user) Say(sentence string) {
u.sayer.Say(sentence)
}
type Sayer interface {
Say(string)
}
type sayer struct{}
func (s sayer) Say(sentence string) {
fmt.Println(sentence)
}
user_test.go
package user
import (
"testing"
)
type mockSayer struct {
called int
calledArg string
}
func (s *mockSayer) Say(sentence string) {
s.called++
s.calledArg = sentence
}
func TestHello(t *testing.T) {
mockSayer := &mockSayer{}
u := user{sayer: mockSayer}
u.Hello()
if mockSayer.called != 1 {
t.Fatalf("not called")
}
if mockSayer.calledArg != "Hello" {
t.Fatalf("wrong arg")
}
}
我了解大多数的情况下,人们会建议使用第二种方法,因为这是依赖注入围棋是如何工作的。
但是,在这个例子中,将Say
的实现提取到另一个层(我认为不必要的复杂性)很奇怪。
有没有更好的解决方案来解决这种依赖? 或你喜欢哪种方法,为什么?
遵循你的推理,我们应该在订单级别测试每个功能的完整性。 我们测试函数的结果而不是积分,所以测试应该只检查函数'Hello'的输出。 –
当有一个函数'Hello',我需要测试它的正确性。通常,我会做两件事。第一个是声明输出的正确性(黑盒测试类型),第二个是声明calledCount,如果'Hello'调用任何其他函数(白盒测试类),则调用Param。 这个问题更关注于如何在不影响正常开发体验的情况下优雅地完成第二个任务(我总结的两种方法由于不必要的复杂性而是不好的开发经验) – Even