Golang
でテストを書く際にgomockを使用したのですが、使い方がよくわからなかったのでまとめてみます。
1. gomockとは
テスト時に作るモックを自動生成してくれるツール。
1からモックを作るのも良いですが、自動生成してもらえるだけで効率的にテストを進められます。
2. インストール
$ go get github.com/golang/mock/gomock
$ go get github.com/golang/mock/mockgen
3. テスト対象のコード
以下のコードのGetAccessFunc()
をテストしてみたいと思います。
package getaccess
import "gomock-sample/httpaccess"
type message struct {
message string
}
type GetAccess interface {
GetAccessFunc() (statuscode int, err error)
}
type getaccess struct {
Ta httpaccess.TestAPI
}
func NewGetAccess(ta httpaccess.TestAPI) GetAccess {
return &getaccess{ta}
}
func (ga getaccess) GetAccessFunc() (statuscode int, err error) {
var responceJSON message
statuscode, err = ga.Ta.GET("/auth/test", "authorizationkey", &responceJSON)
if err != nil {
return statuscode, err
}
return statuscode, nil
}
途中でGET()
を呼んでいますが、中身を見てみましょう。
package httpaccess
import (
"encoding/json"
"io/ioutil"
"net/http"
)
type TestAPI interface {
GET(urlString string, authorization string, responseJSON interface{}) (statuscode int, err error)
}
type testapi struct {
BaseURL string
}
func NewTestAPI(baseURL string) TestAPI {
return &testapi{baseURL}
}
// GET return error
func (api testapi) GET(urlString string, authorization string, responseJSON interface{}) (statuscode int, err error) {
url := api.BaseURL + urlString
request, err := http.NewRequest("GET", url, nil)
request.Header.Add("Authorization", authorization)
request.Header.Add("Content-Type", "application/json")
client := new(http.Client)
response, err := client.Do(request)
if err != nil {
return http.StatusBadRequest, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return response.StatusCode, err
}
json.Unmarshal(body, &responseJSON)
return response.StatusCode, nil
}
GET()
は外部のAPIを実行するための処理であるため、テストを書く時点では一度呼びたくない。
httpAccess.go
をmock化してテストを書いていきます。
4. mockの生成
以下をコマンドラインで実行します。
$ mockgen -source httpaccess/httpAccess.go -destination mock_httpaccess/httpAccess_mock.go
-source
でコードを出力、-destination
でファイルに出力します
生成されたファイルを見てみます。
// Code generated by MockGen. DO NOT EDIT.
// Source: httpaccess/httpAccess.go
// Package mock_httpaccess is a generated GoMock package.
package mock_httpaccess
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockTestAPI is a mock of TestAPI interface
type MockTestAPI struct {
ctrl *gomock.Controller
recorder *MockTestAPIMockRecorder
}
// MockTestAPIMockRecorder is the mock recorder for MockTestAPI
type MockTestAPIMockRecorder struct {
mock *MockTestAPI
}
// NewMockTestAPI creates a new mock instance
func NewMockTestAPI(ctrl *gomock.Controller) *MockTestAPI {
mock := &MockTestAPI{ctrl: ctrl}
mock.recorder = &MockTestAPIMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockTestAPI) EXPECT() *MockTestAPIMockRecorder {
return m.recorder
}
// GET mocks base method
func (m *MockTestAPI) GET(urlString, authorization string, responseJSON interface{}) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GET", urlString, authorization, responseJSON)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GET indicates an expected call of GET
func (mr *MockTestAPIMockRecorder) GET(urlString, authorization, responseJSON interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GET", reflect.TypeOf((*MockTestAPI)(nil).GET), urlString, authorization, responseJSON)
}
httpAccess.go
のGET()
と同じinterfaceのmockが生成されています。
テスト実行時にGET()
をmockの中身に差し替えます。
5. テストコードを書く
ステータスコードが200,エラーがnilで帰ってくることを確認するコードを書いてみます。
ctrl := gomock.NewController(t)
defer ctrl.Finish()
testMock := mock.NewMockTestAPI(ctrl)
var responceJSON message
testMock.EXPECT().GET("/auth/test", "authorizationkey", &responceJSON).Return(http.StatusOK, nil)
生成したmockの設定を行っていきます。
EXPECT()
を使用することで、モックのメソッドが呼び出されたのかテストできます。
GET()
の引数で期待される引数を設定。
Return
の引数でmockの戻り値を設定。
元々のGET()
の戻り値はstatuscodeとerrなので、この戻り値に合うように設定しています。
test := NewGetAccess(testMock)
statuscode, err := test.GetAccessFunc()
getAccess()で必要とされているinterfaceにteskmockを入れていきます。
test.GetAccessFunc()
と呼び出すことで、mockのGETを使用してテストを行えます。
最終的なコードは以下の通り。
package getaccess
import (
mock "gomock-sample/mock_httpaccess"
"net/http"
"testing"
"github.com/golang/mock/gomock"
)
func TestGetAccessFunc(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
testMock := mock.NewMockTestAPI(ctrl)
var responceJSON message
testMock.EXPECT().GET("/auth/test", "authorizationkey", &responceJSON).Return(http.StatusOK, nil)
test := NewGetAccess(testMock)
statuscode, err := test.GetAccessFunc()
if err != nil {
t.Error("failed Test")
}
if statuscode != http.StatusOK {
t.Error("failed Test")
}
}
今回はテストしても微妙なコードで解説しましたが、mockで戻り値を固定化することにより、実際の実行結果に左右されず、エラーハンドリングをテストすることが容易になります。
今回のコードはgithubに実行できる状態で置いてありますので、実行してみてください。
https://github.com/yuina1056/gomock-sample