在Go中使用模拟功能 [英] Mock functions in Go

查看:112
本文介绍了在Go中使用模拟功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我通过编写一个小型个人项目来学习Go。虽然它很小,但我决定从一开始就进行严格的单元测试,以在Go上学习良好的习惯。



平凡的单元测试都很好,很花哨,现在困扰于依赖关系;我希望能够用模拟函数替换一些函数调用。以下是我的代码片段:

  func get_page(url字符串)字符串{
get_dl_slot(url)
推迟free_dl_slot(url)

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

内容,错误:= ioutil.ReadAll(resp.Body)
if err!= nil {return}
返回字符串(内容)
}

func下载器(){
dl_slots = make(chan bool,DL_SLOT_AMOUNT)//初始化下载插槽信号量
content:= get_page(BASE_URL)
links_regexp: = regexp.MustCompile(LIST_LINK_REGEXP)
匹配:= links_regexp.FindAllStringSubmatch(content,-1)
for _,match:=范围匹配{
go serie_dl(match [1],match [ 2])
}
}

我希望能够测试downloader()而不通过http实际获取页面 - 即通过嘲弄get_page(更容易,因为它只返回页面内容作为st ring)或http.Get()。



我发现这个主题: https://groups.google.com/forum/#!topic/golang-nuts/6AN1E2CJOxI 这似乎是一个类似的问题。 Julian Phillips将他的图书馆Withmock( http://github.com/qur/withmock )作为解决方案,但我是无法让它工作。以下是我的测试代码的相关部分,这是对我来说主要是货物邪教代码,说实话:
$ b

  import(
测试
净/ http//模拟
code.google.com/p/gomock

...
func TestDownloader (t * testing.T){
ctrl:= gomock.NewController()
推迟ctrl.Finish()
http.MOCK()。SetController(ctrl)
http。 EXPECT()。Get(BASE_URL)
downloader()
//其余的被写入
}

测试输出如下:

 错误:未能安装'_et / http' :退出状态1 
输出:
无法加载软件包:package _et / http:在/ var / folders / z9 / ql_yn5h550s6shtb9c5sggj40000gn中找到软件包http(chunked.go)和main(main_mock.go) T / withmock570825607 / path / src / _et / http

Withmock是我测试问题的解决方案吗?我应该怎么做才能使它工作?

解决方案

:)



就个人而言,我不使用 gomock (或者任何模拟框架;嘲笑Go没有它很容易)。我要么将依赖项传递给 downloader()函数作为参数,要么将 downloader() a方法,类型可以包含 get_page 依赖项:方法1:传递 get_page()作为下载程序()的参数

 类型PageGetter func(url字符串)字符串

func下载器(pageGetterFunc PageGetter){
// ...
content:= pageGetterFunc(BASE_URL)
// ...
}

主营:

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

func main(){
downloader(get_page)
}

测试:
$ b

  func mock_get_page(url string)string {
//模拟您的'get_page()'函数


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



方法2:制作 download()类型下载器的方法



如果您不想将依赖项作为参数传递,也可以使 get_page() 类型的成员,并使 download()该类型的方法,然后可以使用 get_page < code $ c
$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $'
get_page PageGetter
}

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

func(d * Downloader)下载(){
// ...
内容:= 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 {
//模拟您的'get_page()'函数
}
$ b $ func TestDownloader(){
d:= NewDownloader(mock_get_page)
d.download()
}


I'm learning Go by coding a small personal project. Even though it's small, I decided to do rigorous unit testing to learn good habits on Go right from the start.

Trivial unit tests were all fine and dandy, but I'm puzzled with dependencies now; I want to be able to replace some function calls with mock ones. Here's a snippet of my code:

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])
    }
}

I'd like to be able to test downloader() without actually getting a page through http - i.e. by mocking either get_page (easier since it returns just the page content as a string) or http.Get().

I found this thread: https://groups.google.com/forum/#!topic/golang-nuts/6AN1E2CJOxI which seems to be about a similar problem. Julian Phillips presents his library, Withmock (http://github.com/qur/withmock) as a solution, but I'm unable to get it to work. Here's the relevant parts of my testing code, which is largely cargo cult code to me, to be honest:

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
}

The test output is following:

ERROR: Failed to install '_et/http': exit status 1
output:
can't load package: package _et/http: found packages http (chunked.go) and main (main_mock.go) in /var/folders/z9/ql_yn5h550s6shtb9c5sggj40000gn/T/withmock570825607/path/src/_et/http

Is the Withmock a solution to my testing problem? What should I do to get it to work?

解决方案

Kudos to you for practicing good testing! :)

Personally, I don't use gomock (or any mocking framework for that matter; mocking in Go is very easy without it). I would either pass a dependency to the downloader() function as a parameter, or I would make downloader() a method on a type, and the type can hold the get_page dependency:

Method 1: Pass get_page() as a parameter of downloader()

type PageGetter func(url string) string

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

Main:

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

func main() {
    downloader(get_page)
}

Test:

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

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

Method2: Make download() a method of a type Downloader:

If you don't want to pass the dependency as a parameter, you could also make get_page() a member of a type, and make download() a method of that type, which can then use 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)
    //...
}

Main:

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

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

Test:

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

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

这篇关于在Go中使用模拟功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆