我正在通过编写一个小型个人项目来学习Go。即使很小,我还是决定从头开始进行严格的单元测试,以学习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()。
我找到了这个线程:https : //groups.google.com/forum/#!topic/golang- nuts/ 6AN1E2CJOxI,这似乎与一个类似的问题有关。朱利安·菲利普斯(Julian Phillips)提出了他的图书馆Withmock(http://github.com/qur/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 }
测试输出如下:
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
Withmock是否可以解决我的测试问题?我应该怎么做才能使其正常工作?
感谢您练习良好的测试!:)
就我个人而言,我不使用gomock(或任何模拟框架;没有它,Go中的模拟非常容易)。我要么将依赖项downloader()作为参数传递给函数,要么downloader()在类型上创建方法,并且该类型可以容纳该get_page依赖项:
gomock
downloader()
get_page
get_page()
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) }
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() }