randomAccount()
函數建立一個隨機的測試帳戶。gomock.NewController(t)
建立一個新的 gomock.Controller
,此控制器允許您設置和驗證期望的 mock 方法調用。mockdb.NewMockStore(ctrl)
創建一個 mock 的資料庫存儲。store.EXPECT().GetAccount(...)
設定一個 stub,表示當 GetAccount
方法被呼叫時,它應該使用任意的 context 和特定的 account.ID
。NewServer(store)
建立一個模擬的 HTTP 伺服器。這個伺服器不會真的啟動,但它會模擬 API 請求和響應。server.router.ServeHTTP(recorder, request)
處理此請求,並捕獲模擬伺服器的響應。require
函數檢查響應的狀態碼和響應主體,以確保它們與預期的結果匹配。func TestGetAccountAPI(t *testing.T) {
account := randomAccount()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
store := mockdb.NewMockStore(ctrl)
store.EXPECT().
GetAccount(gomock.Any(), gomock.Eq(account.ID)).
Times(1).
Return(account, nil)
server := NewServer(store)
recorder := httptest.NewRecorder()
url := fmt.Sprintf("/accounts/%d", tc.accountID)
request, err := http.NewRequest(http.MethodGet, url, nil)
require.NoError(t, err)
server.router.ServeHTTP(recorder, request)
require.Equal(t, http.StatusOK, recorder.Code)
requireBodyMatchAccount(t, recorder.Body, account)
}
Get Account API 單元測試
account_test.go
在 api package 內。TestGetAccountAPI()
採用 testing.T
輸入參數。func TestGetAccountAPI(t *testing.T) {
}
隨機產生賬戶
randomAccount()
函數來生成隨機的賬戶。util.RandomOwner()
,Balance 使用 util.RandomMoney()
,Currency 使用 util.RandomCurrency()
。func randomAccount() db.Account {
return db.Account{
ID: util.RandomInt(1, 1000),
Owner: util.RandomOwner(),
Balance: util.RandomMoney(),
Currency: util.RandomCurrency(),
}
}
使用 MockStore 進行測試
randomAccount()
生成新賬戶。gomock.NewController
,並傳入 testing.T
物件。defer ctrl.Finish()
確保 controller 的結束方法被呼叫。mockdb.NewMockStore(ctrl)
創建一個新的 mock store。func TestGetAccountAPI(t *testing.T) {
account := randomAccount()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
store := mockdb.NewMockStore(ctrl)
}
建立 mock store 的 stubs
store.EXPECT().GetAccount()
創建 stub。Times(1)
確定這個函數只被呼叫一次。Return(account, nil)
指定當 GetAccount()
被呼叫時返回的值。func TestGetAccountAPI(t *testing.T) {
account := randomAccount()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
store := mockdb.NewMockStore(ctrl)
store.EXPECT().
GetAccount(gomock.Any(), gomock.Eq(account.ID)).
Times(1).
Return(account, nil)
}
建立和測試 HTTP 伺服器
NewServer(store)
創建新伺服器。httptest.NewRecorder()
創建新的 ResponseRecorder。func TestGetAccountAPI(t *testing.T) {
...
server := NewServer(store)
recorder := httptest.NewRecorder()
url := fmt.Sprintf("/accounts/%d", tc.accountID)
request, err := http.NewRequest(http.MethodGet, url, nil)
require.NoError(t, err)
}
擴充測試
requireBodyMatchAccount()
來比較回應主體和期望的 account 物件。ioutil.ReadAll()
從回應中讀取所有數據。json.Unmarshal
將數據解析為 account 物件,然後與期望的 account 物件進行比較。func requireBodyMatchAccount(t *testing.T, body *bytes.Buffer, account db.Account) {
data, err := ioutil.ReadAll(body)
require.NoError(t, err)
var gotAccount db.Account
err = json.Unmarshal(data, &gotAccount)
require.NoError(t, err)
require.Equal(t, account, gotAccount)
}
gomock.Eq(account.ID)
作用是什麼呢 能直接使用account.ID
?
gomock.Eq(account.ID)
是 GoMock 的一個匹配器。它用於檢查傳遞給 mock 方法的參數是否等於指定的值。在這個情境中,account.ID
?這是因為 GoMock 需要一種方式來描述和比較期望的參數。匹配器提供了一個彈性的方法來描述這些期望,它不僅僅限於等於某個值,還可以是一個範圍、一個條件等等。例如,你可以有 gomock.Any()
表示你不關心特定的值,只是期望該方法被調用。gomock.Eq(account.ID)
,你告訴 GoMock:「我期望 GetAccount
方法被調用時,它的 ID 參數必須是account.ID
」。這給了你更多的控制權和描述能力,使得 mock 更具有描述性和彈性。gomock.NewController
是什麼?
gomock.NewController
就像是GoMock中一個指揮官,他管理和監控這些模擬部件的行為。gomock.NewController
幫助我們建立和跟踪模擬部件。