1 回答

TA貢獻1871條經驗 獲得超13個贊
GO 中 mocks 和 stubs 的意圖與不同的編程語言相同:
存根替代了代碼中將在測試執行期間使用的某些依賴項。它通常是為一個特定的測試而構建的,不太可能被重新用于另一個測試,因為它有硬編碼的期望和假設。
mock將存根提升到一個新的水平。它增加了配置方式,因此您可以為不同的測試設置不同的期望。這使得模擬更加復雜,但可重復用于不同的測試。
讓我們檢查一下它是如何工作的示例:
在我們的例子中,我們有 http 處理程序,它在內部對另一個 Web 服務進行 http 調用。為了測試處理程序,我們希望將處理程序代碼與我們無法控制的依賴項(外部 Web 服務)隔離開來。我們可以通過使用stub
或來做到這一點mock
。
stub
我們的處理程序代碼對于和是相同的mock
。我們應該注入http.Client
依賴關系以便能夠在單元測試中隔離它:
func New(client http.Client) http.Handler {
? ? return &handler{
? ? ? ? client: client,
? ? }
}
type handler struct {
? ? client http.Client
}
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
? ? ...
? ? // work with external web service that cannot be executed in unit test
? ? resp, err := h.client.Get("http://example.com")
? ? ...
}
我們對運行時的替代http.Client是直接的stub:
func TestHandlerStub(t *testing.T) {
? ? mux := http.NewServeMux()
? ? mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
? ? ? ? // here you can put assertions for request
? ? ? ? // generate response
? ? ? ? w.WriteHeader(http.StatusOK)
? ? }))
? ? server := httptest.NewServer(mux)
? ? r, _ := http.NewRequest(http.MethodGet, "https://some.com", nil)
? ? w := httptest.NewRecorder()
? ? sut := New(server.Client())
? ? sut.ServeHTTP(w, r)
? ? //assert handler response
}
模擬故事更復雜。我正在跳過模擬實現的代碼,但它的界面可能是這樣的:
type Mock interface {
? ? AddExpectation(path string, handler http.HandlerFunc)
? ? Build() *http.Client
}
這是使用 Mock 進行測試的代碼:
func TestHandlerMock(t *testing.T) {
? ? mock := NewMock()
? ? mock.AddExpectation("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
? ? ? ? // here you can put assertions for request
? ? ? ? // generate response
? ? ? ? w.WriteHeader(http.StatusOK)
? ? }))
? ? r, _ := http.NewRequest(http.MethodGet, "https://some.com", nil)
? ? w := httptest.NewRecorder()
? ? sut := New(mock.Build())
? ? sut.ServeHTTP(w, r)
? ? //assert handler response
}
對于這個簡單的示例,它沒有增加太多價值。但是想想更復雜的情況。您可以構建更簡潔的測試代碼,并用更少的行覆蓋更多的案例。
如果我們必須調用 2 個服務并稍微改進我們的模擬,這就是測試設置的樣子:
mock.AddExpectation("/first", firstSuccesfullHandler).AddExpectation("/second", secondSuccesfullHandler)
mock.AddExpectation("/first", firstReturnErrorHandler).AddExpectation("/second", secondShouldNotBeCalled)
mock.AddExpectation("/first", firstReturnBusy).AddExpectation("/first", firstSuccesfullHandler)AddExpectation("/second", secondSuccesfullHandler)
您可以想象,如果我們沒有我們的微型模擬助手,您必須在測試中復制粘貼處理程序邏輯多少次。那些復制粘貼的代碼使我們的測試變得脆弱。
但是構建自己的模擬并不是唯一的選擇。您可以依賴現有的模擬包,例如模擬 SQL 的DATA-DOG/go-sqlmock 。
- 1 回答
- 0 關注
- 195 瀏覽
添加回答
舉報