给定:
// FileReader interface for reading from a file
type FileReader interface {
ReadFile(filename string) ([]byte, error)
}
type FileRead struct {}
// ReadFile reads from filename fn using ioutilReadFile
func (fr FileRead) ReadFile(fn string) ([]byte, error) {
return ioutil.ReadFile(fn)
}
type Dev struct {
*FileRead
}
func NewDev() *Dev {
frd := FileRead{}
return &Dev{frd}
}
// Function that does some job
func (dev Dev) DoSomeStuff() {
//...
dev.ReadFile("file")
//...
}
func main () {
doer := NewDev()
doer.DoSomeStuff()
}
在单元测试期间,应该模拟 ReadFile 操作。如何在 go test 中最好地实现这一目标?
Dev struct 可以改为使用 FileReader 接口(interface),但随后不再使用结构嵌入,DoSomeStuff 中的语法变得不那么明显。
最佳答案
如果您使用的是 Dargo 等 DI 框架您可以将实现 FileReader 的东西注入(inject)到开发中。在您的主线代码中,您将绑定(bind)普通的 FileReader,但在您的测试中,您可以使用模拟 FileReader。您的其余代码不应该知道其中的区别。它看起来像这样:
import (
"github.com/jwells131313/dargo/ioc"
"io/ioutil"
"testing"
)
type FileReader interface {
ReadFile(filename string) ([]byte, error)
}
type FileRead struct{}
// ReadFile reads from filename fn using ioutilReadFile
func (fr FileRead) ReadFile(fn string) ([]byte, error) {
return ioutil.ReadFile(fn)
}
type Dev struct {
MyReader FileReader `inject:"FileReader"`
}
/* Not here anymore, but you can implement DargoInitializer
if you need more initialization of Dev
func NewDev() *Dev {
frd := FileRead{}
return &Dev{frd}
}
*/
// Function that does some job
func (dev Dev) DoSomeStuff() {
//...
dev.MyReader.ReadFile("file")
//...
}
var locator ioc.ServiceLocator
func initialize() error {
l, err := ioc.CreateAndBind("Example", func(binder ioc.Binder) error {
binder.Bind("Dev", &Dev{})
binder.Bind("FileReader", &FileRead{})
return nil
})
locator = l
return err
}
func main() {
initialize()
raw, _ := locator.GetDService("Dev")
doer := raw.(*Dev)
doer.DoSomeStuff()
}
// Here is test code
type TestFileRead struct{}
// ReadFile is a mock that just returns a zero-length byte array
func (tfr TestFileRead) ReadFile(fn string) ([]byte, error) {
return []byte{}, nil
}
func TestMe(t *testing.T) {
initialize()
ioc.BindIntoLocator(locator, func(binder ioc.Binder) error {
binder.Bind("FileReader", &TestFileRead{}).Ranked(1)
return nil
})
// Test Me!
}
在上面的例子中,“普通”文件读取器被注入(inject)到普通代码中,但在测试中有一个更高级别的 TestFileReader。因此,当您在测试中获取 Dev 时,它将被注入(inject)测试代码,而不是来自主线代码的代码。
希望这对您有所帮助。
关于unit-testing - 在go测试中模拟嵌入式结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52430510/