duanqiao9541 2013-10-03 19:47
浏览 46
已采纳

Go中的模拟功能

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?

  • 写回答

5条回答 默认 最新

  • douping3427 2013-10-03 20:42
    关注

    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()
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

悬赏问题

  • ¥15 Arcgis相交分析无法绘制一个或多个图形
  • ¥15 seatunnel-web使用SQL组件时候后台报错,无法找到表格
  • ¥15 fpga自动售货机数码管(相关搜索:数字时钟)
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型
  • ¥15 vs2019中数据导出问题
  • ¥20 云服务Linux系统TCP-MSS值修改?
  • ¥20 关于#单片机#的问题:项目:使用模拟iic与ov2640通讯环境:F407问题:读取的ID号总是0xff,自己调了调发现在读从机数据时,SDA线上并未有信号变化(语言-c语言)