您好,登錄后才能下訂單哦!
本篇內容介紹了“Golang如何實現單元測試中的邏輯層”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
安裝 go install github.com/golang/mock/mockgen@v1.6.0
首先我們還是基于上一次的例子,這里給出上一次例子中所用到的接口
package service import ( "context" "fmt" "go-demo/m/unit-test/entity" ) type UserRepo interface { AddUser(ctx context.Context, user *entity.User) (err error) DelUser(ctx context.Context, userID int) (err error) GetUser(ctx context.Context, userID int) (user *entity.User, exist bool, err error) } type UserService struct { userRepo UserRepo } func NewUserService(userRepo UserRepo) *UserService { return &UserService{userRepo: userRepo} } func (us *UserService) AddUser(ctx context.Context, username string) (err error) { if len(username) == 0 { return fmt.Errorf("username not specified") } return us.userRepo.AddUser(ctx, &entity.User{Username: username}) } func (us *UserService) GetUser(ctx context.Context, userID int) (user *entity.User, err error) { userInfo, exist, err := us.userRepo.GetUser(ctx, userID) if err != nil { return nil, err } if !exist { return nil, fmt.Errorf("user %d not found", userID) } return userInfo, nil }
可以看到我們的目標很明確,就是需要 mock 掉 UserRepo
接口的幾個方法,就可以測試我們 AddUser
和 GetUser
方法了
使用 mockgen
命令可以生成我們所需要的 mock 接口
mockgen -source=./service/user.go -destination=./mock/user_mock.go -package=mock
參數名稱都很好理解,我這邊不贅述了。命令執行完成之后,會在 destination
生成對于的 mock 接口,就可以使用了。
生成的代碼大致如下面的樣子,可以簡單瞄一眼:
// Code generated by MockGen. DO NOT EDIT. // Source: ./user.go // Package mock is a generated GoMock package. package mock import ( context "context" entity "go-demo/m/unit-test/entity" reflect "reflect" gomock "github.com/golang/mock/gomock" ) // MockUserRepo is a mock of UserRepo interface. type MockUserRepo struct { ctrl *gomock.Controller recorder *MockUserRepoMockRecorder } // MockUserRepoMockRecorder is the mock recorder for MockUserRepo. type MockUserRepoMockRecorder struct { mock *MockUserRepo } // NewMockUserRepo creates a new mock instance. func NewMockUserRepo(ctrl *gomock.Controller) *MockUserRepo { mock := &MockUserRepo{ctrl: ctrl} mock.recorder = &MockUserRepoMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockUserRepo) EXPECT() *MockUserRepoMockRecorder { return m.recorder } // AddUser mocks base method. func (m *MockUserRepo) AddUser(ctx context.Context, user *entity.User) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddUser", ctx, user) ret0, _ := ret[0].(error) return ret0 } // AddUser indicates an expected call of AddUser. func (mr *MockUserRepoMockRecorder) AddUser(ctx, user interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUser", reflect.TypeOf((*MockUserRepo)(nil).AddUser), ctx, user) } // DelUser mocks base method. func (m *MockUserRepo) DelUser(ctx context.Context, userID int) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DelUser", ctx, userID) ret0, _ := ret[0].(error) return ret0 } // DelUser indicates an expected call of DelUser. func (mr *MockUserRepoMockRecorder) DelUser(ctx, userID interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelUser", reflect.TypeOf((*MockUserRepo)(nil).DelUser), ctx, userID) } // GetUser mocks base method. func (m *MockUserRepo) GetUser(ctx context.Context, userID int) (*entity.User, bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUser", ctx, userID) ret0, _ := ret[0].(*entity.User) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // GetUser indicates an expected call of GetUser. func (mr *MockUserRepoMockRecorder) GetUser(ctx, userID interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockUserRepo)(nil).GetUser), ctx, userID) }
gomock
的單元測試編寫起來也很方便,只需要調用 EXPECT()
方法,將需要 mock 的接口對應需要的返回值就可以了。我們直接來看例子:
package service import ( "context" "testing" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "go-demo/m/unit-test/entity" "go-demo/m/unit-test/mock" ) func TestUserService_AddUser(t *testing.T) { ctl := gomock.NewController(t) defer ctl.Finish() mockUserRepo := mock.NewMockUserRepo(ctl) userInfo := &entity.User{Username: "LinkinStar"} // 無論對 AddUser 方法輸入任意參數,均會返回 userInfo 信息 mockUserRepo.EXPECT().AddUser(gomock.Any(), gomock.Any()).Return(nil) userService := NewUserService(mockUserRepo) err := userService.AddUser(context.TODO(), userInfo.Username) assert.NoError(t, err) } func TestUserService_GetUser(t *testing.T) { ctl := gomock.NewController(t) defer ctl.Finish() userID := 1 username := "LinkinStar" mockUserRepo := mock.NewMockUserRepo(ctl) // 只有當對于 GetUser 傳入 userID 為 1 時才會返回 user 信息 mockUserRepo.EXPECT().GetUser(context.TODO(), userID).Return(&entity.User{ ID: userID, Username: username, }, true, nil) userService := NewUserService(mockUserRepo) userInfo, err := userService.GetUser(context.TODO(), userID) assert.NoError(t, err) assert.Equal(t, username, userInfo.Username) }
與之前一樣,我們依舊使用 github.com/stretchr/testify
做斷言來驗證最終結果。可以看到,單元測試編寫起來并不難。
當然,如果我們每次修改接口或者新增接口都需要重新執行一次命令,一個文件還好,當有很多文件的時候肯定是非常困難的。所以我們需要使用 go:generate 來優化一下。
我們可以在需要 mock 的接口上方加入注釋(注意這里寫的路徑要和實際路徑相符合):
//go:generate mockgen -source=./user.go -destination=../mock/user_mock.go -package=mock type UserRepo interface { AddUser(ctx context.Context, user *entity.User) (err error) DelUser(ctx context.Context, userID int) (err error) GetUser(ctx context.Context, userID int) (user *entity.User, exist bool, err error) }
然后只需要使用命令
go generate ./...
就可以生成全部的 mock 嘞,所以及時文件很多,只需要利用好 go:generate 也能一次搞定。
“Golang如何實現單元測試中的邏輯層”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。