iT邦幫忙

2023 iThome 鐵人賽

DAY 23
0
自我挑戰組

Techschool Goalng Backend Master Class 的學習記錄系列 第 23

[Day 23] Mock DB for testing HTTP API in Go and achieve 100% coverage Part 2

  • 分享至 

  • xImage
  •  

Generate mock DB

  1. 既然我們已經有了 db.Store interface,我們可以使用 gomock 生成 mock interface

  2. 首先,我將在 db 套件內創建一個新的 mock 文件夾。接著,打開終端機並執行:

    mkdir -p db/mock
    mockgen -help
    
  3. mockgen 提供了兩種方式生成mocks:

    • Source mode:從單一源文件生成 mock 介面。
    • Reflect mode:只需提供包名和介面名稱,讓 mockgen 使用反射自動完成。
  4. 選擇使用 Reflect mode 並執行以下命令:

    • Store Interface的導入路徑 (github.com/Kcih4518/simpleBank_2023/db/sqlc)。
    • 使用的Interface Name,即 Store
    • destination是指定生成輸出文件的目的地: db/mock/store.go
    • package 指定的package name (mockdb)
    mockgen -build_flags=--mod=mod -destination db/mock/store.go -package mockdb github.com/Kcih4518/simpleBank_2023/db/sqlc Store
    
  5. 回到 Visual Studio Code,我們可以看到在 db/mock 文件夾內生成了一個新的 store.go 文件。

// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/Kcih4518/simpleBank_2023/db/sqlc (interfaces: Store)

// Package mockdb is a generated GoMock package.
package mockdb

import (
	context "context"
	reflect "reflect"

	db "github.com/Kcih4518/simpleBank_2023/db/sqlc"
	gomock "github.com/golang/mock/gomock"
)

// MockStore is a mock of Store interface.
type MockStore struct {
	ctrl     *gomock.Controller
	recorder *MockStoreMockRecorder
}

// MockStoreMockRecorder is the mock recorder for MockStore.
type MockStoreMockRecorder struct {
	mock *MockStore
}

// NewMockStore creates a new mock instance.
func NewMockStore(ctrl *gomock.Controller) *MockStore {
	mock := &MockStore{ctrl: ctrl}
	mock.recorder = &MockStoreMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockStore) EXPECT() *MockStoreMockRecorder {
	return m.recorder
}

...
  1. 這個文件中有兩個重要的結構:MockStoreMockStoreMockRecorder

    • MockStore:實現了 Store Interface的所有必需功能。
    • MockStoreMockRecorder:用於建立stubs,可以指定應該呼叫 AddAccountBalance() 函數多少次,以及參數的值。
    // AddAccountBalance mocks base method.
    func (m *MockStore) AddAccountBalance(arg0 context.Context, arg1 db.AddAccountBalanceParams) (db.Account, error) {
    	m.ctrl.T.Helper()
    	ret := m.ctrl.Call(m, "AddAccountBalance", arg0, arg1)
    	ret0, _ := ret[0].(db.Account)
    	ret1, _ := ret[1].(error)
    	return ret0, ret1
    }
    
    // AddAccountBalance indicates an expected call of AddAccountBalance.
    func (mr *MockStoreMockRecorder) AddAccountBalance(arg0, arg1 interface{}) *gomock.Call {
    	mr.mock.ctrl.T.Helper()
    	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddAccountBalance", reflect.TypeOf((*MockStore)(nil).AddAccountBalance), arg0, arg1)
    }
    
  2. 在開始使用新生成的 MockStore 寫API測試之前,我會在 Makefile 中添加一個新的 mock 命令,這樣我們可以隨時重新生成代碼。

    mock:
        mockgen -build_flags=--mod=mod -destination db/mock/store.go -package mockdb github.com/Kcih4518/simpleBank_2023/db/sqlc Store
    
    .PHONY: postgres createdb dropdb migrateup migratedown sqlc test server mock
    
    
  3. 現在,每當我們想重新生成 mock store,我們可以簡單地在終端機中運行 make mock

Q & A:

  1. 如何確認MockStore StructStore Interfaceimplementation ?
    1. 為了確定 MockStore 是否實現了 Store Interface的每個方法,您需要將 MockStore 的方法與 Store 介面中定義的方法進行比較。如果 MockStore 對於 Store 中的每個方法都有相應的方法,且每個方法都有正確的Signature(函數名稱、它的參數類型列表和它的返回值類型),那麼 MockStore 就完全實現了 Store 介面。

      db/sqlc/store.go
      type Store interface {
      	Querier
      	TransferTx(ctx context.Context, arg TransferTxParams) (TransferTxResult, error)
      }
      
      db/sqlc/querier.go
      
      // Code generated by sqlc. DO NOT EDIT.
      // versions:
      //   sqlc v1.20.0
      
      package db
      
      import (
      	"context"
      )
      
      type Querier interface {
      	AddAccountBalance(ctx context.Context, arg AddAccountBalanceParams) (Account, error)
      	CreateAccount(ctx context.Context, arg CreateAccountParams) (Account, error)
      	CreateEntry(ctx context.Context, arg CreateEntryParams) (Entry, error)
      	CreateTransfer(ctx context.Context, arg CreateTransferParams) (Transfer, error)
      	DeleteAccount(ctx context.Context, id int64) error
      	GetAccount(ctx context.Context, id int64) (Account, error)
      	GetAccountForUpdate(ctx context.Context, id int64) (Account, error)
      	GetEntry(ctx context.Context, id int64) (Entry, error)
      	GetTransfer(ctx context.Context, id int64) (Transfer, error)
      	ListAccounts(ctx context.Context, arg ListAccountsParams) ([]Account, error)
      	ListEntries(ctx context.Context, arg ListEntriesParams) ([]Entry, error)
      	ListTransfers(ctx context.Context, arg ListTransfersParams) ([]Transfer, error)
      	UpdateAccount(ctx context.Context, arg UpdateAccountParams) (Account, error)
      }
      
      var _ Querier = (*Queries)(nil)
      
      db/mock/store.go
      
      func (m *MockStore) AddAccountBalance(arg0 context.Context, arg1 db.AddAccountBalanceParams) (db.Account, error)
      func (m *MockStore) CreateAccount(arg0 context.Context, arg1 db.CreateAccountParams) (db.Account, error)
      func (m *MockStore) CreateTransfer(arg0 context.Context, arg1 db.CreateTransferParams) (db.Transfer, error)
      func (m *MockStore) TransferTx(arg0 context.Context, arg1 db.TransferTxParams) (db.TransferTxResult, error)
      ...
      

上一篇
[Day 22] Mock DB for testing HTTP API in Go and achieve 100% coverage Part 1
下一篇
[Day 24] Mock DB for testing HTTP API in Go and achieve 100% coverage Part 3
系列文
Techschool Goalng Backend Master Class 的學習記錄31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言