您好,登錄后才能下訂單哦!
本篇內容介紹了“go單元測試的方法有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
gomock模擬對象的方式是讓用戶聲明一個接口,然后使用gomock提供的mockgen工具生成mock對象代碼。要模擬(mock)被測試代碼的依賴對象時候,即可使用mock出來的對象來模擬和記錄依賴對象的各種行為:比如最常用的返回值,調用次數等等。文字敘述有點抽象,直接上代碼:
dick.go中DickFunc依賴外部對象OutterObj,本示例就是說明如何使用gomock框架控制所依賴的對象。
func DickFunc( outterObj MockInterface,para int)(result int){ fmt.Println("This init DickFunc") fmt.Println("call outter.func:") return outterObj.OutterFunc(para) }
mockgen工具命令是:
mockgen -source {source_file}.go -destination {dest_file}.go
比如,本示例即是:
mockgen -source src_mock.go -destination dst_mock.go
執行完后,可在同目錄下找到生成的dst_mock.go文件,可以看到mockgen工具也實現了接口:
在本示例中只簡單的更改了返回值,拋磚引玉:
func TestDickFunc(t *testing.T ){ mockCtrl := gomock.NewController(t) //defer mockCtrl.Finish() mockObj := dick.NewMockMockInterface(mockCtrl) mockObj.EXPECT().OutterFunc(3).Return(10) result :=dick.DickFunc(mockObj,3) t.Log("resutl:",result) }
使用go test命令執行這個單測
從結果看:本來應該輸出3,最后輸出就是10,和其他語言mock框架相似,生產出來的Mock對象不用自己去重定義這么麻煩。
由于go在網絡架構上的優秀封裝,使得go在很多網絡場景被廣泛使用,而http協議是其中重要部分,在面對http請求的時候,可以對http的client進行測試,算是mock的特殊應用場景。
看一個簡單的示例就輕松的看懂了:
func TestHttp(t *testing.T) { handler := FruitServer() server := httptest.NewServer(handler) defer server.Close() e := httpexpect.New(t, server.URL) e.GET("/fruits"). Expect(). Status(http.StatusOK).JSON().Array().Empty() }
其中還支持對不同方法(包括Header,Post等)的構造以及返回值Json的自定義,更多細節查看其官網
還有一個testify使用起來可以說兼容了《一》中的gocheck和gomock,但是其mock使用稍微有點煩雜,使用繼承tetify.Mock(匿名組合)重新實現需要Mock的接口,在這個接口里使用者自己使用Called(反射實現)被Mock的接口。
《單元測試的藝術》中認為stub和mock最大的區別就依賴對象是否和被測對象有交互,而從結果看就是樁對象不會使測試失敗,它只是為被測對象提供依賴的對象,并不改變測試結果,而mock則會根據不同的交互測試要求,很可能會更改測試的結果。說了這么多理論,但其實這兩種方法都不是割裂的,所以gomock框架除了像其名字一樣可以模擬對象以外,還提供了樁對象的功能(stub)。以其實現來說,更像是一個樁對象的注入。但是因為兼容了多個有用的功能,所以其在社區最為火爆。
具體用法可參考其github主頁
還有一種比較常見的場景就是和數據庫的交互場景,go-sqlmock是sql模擬(Mock)驅動器,主要用于測試數據庫的交互,go-sqlmock提供了完整的事務的執行測試框架,最新的版本(16.11.02)還支持prepare參數化提交和執行的Mock方案。
比如有這樣的被測函數:
func recordStats(db *sql.DB, userID, productID int64) (err error) { tx, err := db.Begin() if err != nil { return } defer func() { switch err { case nil: err = tx.Commit() default: tx.Rollback() } }() if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil { return } if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil { return } return } func main() { db, err := sql.Open("mysql", "root@/root") if err != nil { panic(err) } defer db.Close() if err = recordStats(db, 1 , 5 ); err != nil { panic(err) } }
單測時:
func TestShouldUpdateStats(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("mock error: '%s' ", err) } defer db.Close() mock.ExpectBegin() mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectExec("INSERT INTO product_viewers") .WithArgs(2, 3) .WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() if err = recordStats(db, 2, 3); err != nil { t.Errorf("exe error: %s", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("not implements: %s", err) } } //測試回滾 func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("mock error: '%s'", err) } defer db.Close() mock.ExpectBegin() mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectExec("INSERT INTO product_viewers") .WithArgs(2, 3) .WillReturnError(fmt.Errorf("some error")) mock.ExpectRollback() // 執行被測方法,有錯 if err = recordStats(db, 2, 3); err == nil { t.Errorf("not error") } // 執行被測方法,mock對象 if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("not implements: %s", err) } }
“go單元測試的方法有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。