doulongsi1831 2018-09-20 17:41
浏览 18

在Go测试中模拟嵌入式结构

Given:

// 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()
}

During unit testing, the ReadFile operation should be mocked. How can one best achieve this in go test?

Dev struct could instead use FileReader interface, but then struct embedding is no longer used and the syntax in DoSomeStuff becomes less obvious.

  • 写回答

1条回答 默认 最新

  • duanbiyi7319 2018-09-20 18:06
    关注

    If you are using a DI framework such as Dargo you can inject something that implements FileReader into dev. In your main-line code you would bind the normal FileReader, but in your test you can use a mock FileReader. The rest of your code should not know the difference. It would look something like this:

    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!
    
    }
    

    In the above example the "normal" file reader is injected in the normal code, but in the test there is a TestFileReader with a higher rank. So when you went to get Dev in the test it would be injected with the test one, not the one from the main-line code.

    Hope this helps.

    评论

报告相同问题?

悬赏问题

  • ¥50 我撰写的python爬虫爬不了 要爬的网址有反爬机制
  • ¥15 Centos / PETSc / PETGEM
  • ¥15 centos7.9 IPv6端口telnet和端口监控问题
  • ¥120 计算机网络的新校区组网设计
  • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作
  • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 海浪数据 南海地区海况数据,波浪数据
  • ¥20 软件测试决策法疑问求解答
  • ¥15 win11 23H2删除推荐的项目,支持注册表等
  • ¥15 matlab 用yalmip搭建模型,cplex求解,线性化处理的方法