unit-testing - 在go测试中模拟嵌入式结构

标签 unit-testing go

给定:

// 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/

相关文章:

c# - 如何使用 NUnit 和 Rhino Mocks 模拟 HttpContext.Current.Items

java - JUnit 异常测试

node.js - 如何测试 Grunt 任务?理解和最佳实践

转到 "unrecognized imports"

unit-testing - Jacoco在我的Gradle项目中未显示Spock代码覆盖率

unit-testing - Grails 单元或集成测试?

http - 如何使用 golang 发送帖子,用这个 curl 命令替换

json - Go-Gorm 中的深层嵌套结构

go - 如何在 Go 模板中访问数组第一个索引的值

go - 如何通过对节点的完全访问权限来解码YAML v3