想象一下,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 - 使用lambda 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")
}
}
我理解大多数情况,人们会建议使用方法2,因为这就是依赖注入在Go中的工作方式。
但是,在这个例子中,将Say
的实现提取到另一层(我认为不必要的复杂性)是很奇怪的。
有没有更好的解决方案来解决这种依赖? 或者您更喜欢哪种方法?为什么?
答案 0 :(得分:3)
以上都不是。我不知道你在哪里证明Hello
方法确实有效,实际上写的是“Hello\n
”。检查Say
方法输出。模仿os.Stdout
。例如,
user.go
:
package user
import (
"fmt"
"io"
"os"
)
type user struct{}
const hello = "Hello"
func (u user) Hello() {
u.Say(hello)
}
var stdwrite = io.Writer(os.Stdout)
func (u user) Say(sentence string) {
fmt.Fprintln(stdwrite, sentence)
}
user_test.go
:
package user
import (
"bytes"
"io"
"testing"
)
func TestHello(t *testing.T) {
u := user{}
u.Hello() // for real
defer func(w io.Writer) { stdwrite = w }(stdwrite)
stdwrite = new(bytes.Buffer)
u.Hello() // for test
got := stdwrite.(*bytes.Buffer).String()
want := hello + "\n"
if got != want {
t.Errorf("want: %q got: %q", want, got)
}
}
输出:
$ go test -v
=== RUN TestHello
Hello
--- PASS: TestHello (0.00s)
PASS
ok say 0.001s