想象有一個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