Go 中的模拟函数

新手上路,请多包涵

我对依赖性感到困惑。我希望能够用模拟函数替换一些函数调用。这是我的代码片段:

 func get_page(url string) string {
    get_dl_slot(url)
    defer free_dl_slot(url)

    resp, err := http.Get(url)
    if err != nil { return "" }
    defer resp.Body.Close()

    contents, err := ioutil.ReadAll(resp.Body)
    if err != nil { return "" }
    return string(contents)
}

func downloader() {
    dl_slots = make(chan bool, DL_SLOT_AMOUNT) // Init the download slot semaphore
    content := get_page(BASE_URL)
    links_regexp := regexp.MustCompile(LIST_LINK_REGEXP)
    matches := links_regexp.FindAllStringSubmatch(content, -1)
    for _, match := range matches{
        go serie_dl(match[1], match[2])
    }
}

我希望能够测试 downloader() 而无需实际通过 http 获取页面 - 即通过 get_page (更容易,因为它只返回页面内容作为字符串)或 http.Get()

我发现 这个线程 似乎是关于类似的问题。 Julian Phillips 展示了他的库 Withmock 作为解决方案,但我无法让它工作。这是我的测试代码的相关部分,老实说,这对我来说主要是货物崇拜代码:

 import (
    "testing"
    "net/http" // mock
    "code.google.com/p/gomock"
)
...
func TestDownloader (t *testing.T) {
    ctrl := gomock.NewController()
    defer ctrl.Finish()
    http.MOCK().SetController(ctrl)
    http.EXPECT().Get(BASE_URL)
    downloader()
    // The rest to be written
}

测试输出如下:

错误:无法安装“_et/http”:退出状态 1 输出:无法加载包:包 _et/http:在中找到包 http (chunked.go) 和 main (main_mock.go)

/var/folders/z9/ql_yn5h550s6shtb9c5sggj40000gn/T/withmock570825607/path/src/_et/http

Withmock 可以解决我的测试问题吗?我应该怎么做才能让它工作?

原文由 GolDDranks 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 329
2 个回答

就个人而言,我不使用 gomock (或与此相关的任何模拟框架;没有它在 Go 中模拟非常容易)。我要么将依赖项传递给 downloader() 函数作为参数,要么我将 downloader() 一个类型的方法,并且该类型可以保存 get_page 依赖:

方法一:传递 get_page() 作为 downloader() 的参数

type PageGetter func(url string) string

func downloader(pageGetterFunc PageGetter) {
    // ...
    content := pageGetterFunc(BASE_URL)
    // ...
}

主要的:

 func get_page(url string) string { /* ... */ }

func main() {
    downloader(get_page)
}

测试:

 func mock_get_page(url string) string {
    // mock your 'get_page()' function here
}

func TestDownloader(t *testing.T) {
    downloader(mock_get_page)
}

方法2:制作 download() 一个类型的方法 Downloader

如果您不想将依赖项作为参数传递,您还可以制作 get_page() 类型的成员,并制作 download() 该类型的方法,然后可以使用 get_page :

 type PageGetter func(url string) string

type Downloader struct {
    get_page PageGetter
}

func NewDownloader(pg PageGetter) *Downloader {
    return &Downloader{get_page: pg}
}

func (d *Downloader) download() {
    //...
    content := d.get_page(BASE_URL)
    //...
}

主要的:

 func get_page(url string) string { /* ... */ }

func main() {
    d := NewDownloader(get_page)
    d.download()
}

测试:

 func mock_get_page(url string) string {
    // mock your 'get_page()' function here
}

func TestDownloader() {
    d := NewDownloader(mock_get_page)
    d.download()
}

原文由 weberc2 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果您将函数定义更改为使用变量:

 var get_page = func(url string) string {
    ...
}

您可以在测试中覆盖它:

 func TestDownloader(t *testing.T) {
    get_page = func(url string) string {
        if url != "expected" {
            t.Fatal("good message")
        }
        return "something"
    }
    downloader()
}

不过要小心,如果其他测试测试您重写的函数的功能,它们可能会失败!

Go 作者在 Go 标准库中使用此模式将测试挂钩插入代码中,使测试更容易:

原文由 Jake 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题