go语言中GoMock的实现

作者:

文章目录
  • 为了使用GoMock,我们需要安装gomock包github.com/golang/mock/gomock和mockgen代码生成工具github.com/golang/mock/mockgen。使用这个命令行: go get github.com/golang/mock/gomock go get github.com/golang/mock/mockgen GoMock的使用遵循四个基本步骤: 使用mockgen为你想模拟的接口生成一个模拟对象。 在测试部分,创建一个gomock.Controller的实例,并把它传递给你的mock对象的构造函数以获得一个mock对象。 在mock上调用EXPECT()来设置期望值和返回值。 在模拟控制器上调用Finish()来断言模拟对象的期望。
  • 让我们创建一个这样的文件夹(本代码在 go1.16.15 版本下执行) gomock_test ├── doer │ └── doer.go ├── mocks │ └── mock_doer.go └── user ├── user.go └── user_test.go doer/doer.go package doer type Doer interface { DoSomething(int, string) error SaySomething(string) string } 那么这里是我们在模拟Doer接口时要测试的代码。 user/user.go package user import “gomock_test/doer” const ( filtered = “OK” unfiltered = “spam” Nice = “nice” Bad = “bad” ) type User struct { // struct while mocking the doer interface Doer doer.Doer } // method Use using it func (u *User) Use() error { return u.Doer.DoSomething(123, “Hello GoMock”) } func (u *User) SaySomething(num int) string { if num == 3 { return u.Doer.SaySomething(unfiltered) } return u.Doer.SaySomething(filtered) } type Student struct{} func (s *Student) DoSomething(_ int, _ string) error { panic(“not implemented”) // TODO: Implement } func (s *Student) SaySomething(kata string) string { if kata == filtered { return Nice } return Bad } 我们将把Doer的模拟放在一个包mocks中。我们首先创建一个包含我们的模拟实现的目录mocks,然后在doer包上运行mockgen: mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer NOTE: 在执行这步的时候,会报错:Failed to format generated source code: mocks/mock_doer.go:5:15: expected ‘;’, found '.’ 这个时候,我们只需要将打印出来的代码复制到我们对应的文件中即可。 当有大量的接口/包需要模拟时,为每个包和接口运行mockgen是一种乌托邦。为了缓解这个问题,可以将mockgen命令与go:generate放在一起。 go:generate mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer 我们必须自己创建目录模拟,因为GoMock不会为我们这样做,而是会以错误退出。 destination=…/mocks/mock_doer.go : 把生成的mocks放在这个路径下。 -package=mocks : 把生成的mocks放在这个包里 gomock_test/doer : 为这个包生成mocks。 Doer : 为这个接口生成mocks(必填),因为我们需要指定哪个接口来生成mocks。(如果需要的话,可以用逗号分隔的列表来指定多个接口。例如,Doer1, Doer2) 因为我们对mockgen的调用在我们的项目中放置了一个文件mocks/mock_doer.go。这就是这样一个生成的mock实现的样子: // Code generated by MockGen. DO NOT EDIT. // Source: github.com/timliudream/go-test/gomock_test/doer (interfaces: Doer) // Package github.com/timliudream/go-test/gomock_test/mocks is a generated GoMock package. package mocks import ( gomock “github.com/golang/mock/gomock” reflect “reflect” ) // MockDoer is a mock of Doer interface. type MockDoer struct { ctrl *gomock.Controller recorder *MockDoerMockRecorder } // MockDoerMockRecorder is the mock recorder for MockDoer. type MockDoerMockRecorder struct { mock *MockDoer } // NewMockDoer creates a new mock instance. func NewMockDoer(ctrl *gomock.Controller) *MockDoer { mock := &MockDoer{ctrl: ctrl} mock.recorder = &MockDoerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockDoer) EXPECT() *MockDoerMockRecorder { return m.recorder } // DoSomething mocks base method. func (m *MockDoer) DoSomething(arg0 int, arg1 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, “DoSomething”, arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // DoSomething indicates an expected call of DoSomething. func (mr *MockDoerMockRecorder) DoSomething(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, “DoSomething”, reflect.TypeOf((*MockDoer)(nil).DoSomething), arg0, arg1) } // SaySomething mocks base method. func (m *MockDoer) SaySomething(arg0 string) string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, “SaySomething”, arg0) ret0, _ := ret[0].(string) return ret0 } // SaySomething indicates an expected call of SaySomething. func (mr *MockDoerMockRecorder) SaySomething(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, “SaySomething”, reflect.TypeOf((*MockDoer)(nil).SaySomething), arg0) } 接下来,我们在测试中定义一个模拟控制器。一个模拟控制器负责跟踪和断言其相关模拟对象的期望。我们可以通过传递一个*testing.T类型的值给它的构造函数来获得一个模拟控制器,并使用它来构造一个Doer接口的模拟对象。 //core of gomock mockCtrl := gomock.NewController(t) //used to trigger final assertions. if its ignored, mocking assertions will never fail defer mockCtrl.Finish() // create a new mock object, passing the controller instance as parameter // for a newly created mock object it will accept any input and outpuite // need to define its behavior with the method expect mockDoer := mocks.NewMockDoer(mockCtrl)
  • 在GoMock中,一个参数可以被期望有一个固定的值,也可以被期望与一个谓词(称为匹配器)相匹配。匹配器用于表示被模拟方法的预期参数范围。下列匹配器在Gomock中被预先定义了: gomock.Any() : 匹配任何值(任何类型)。 gomock.Eq(x) : 使用反射来匹配与x深度相等的值。 gomock.Nil() : 匹配nil user/user_test.go package user import ( “fmt” “github.com/golang/mock/gomock” “gomock_test/mocks” “testing” ) func TestUse(t *testing.T) { //core of gomock mockCtrl := gomock.NewController(t) //used to trigger final assertions. if its ignored, mocking assertions will never fail defer mockCtrl.Finish() // create a new mock object, passing the controller instance as parameter // for a newly created mock object it will accept any input and outpuite // need to define its behavior with the method expect mockDoer := mocks.NewMockDoer(mockCtrl) testUser := &User{Doer: mockDoer} // // Expect Do to be called once with 123 and “Hello GoMock” as parameters, and return nil from the mocked call. mockDoer.EXPECT().DoSomething(123, “Hello GoMock”).Return(nil).Times(1) fmt.Println(testUser.Use()) fmt.Println() } func TestUser_SaySomething(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mockDoer := mocks.NewMockDoer(mockCtrl) user := User{ Doer: mockDoer, } type args struct { num int } tests := []struct { name string args args want string expect func() wantErr bool }{ { name: “Positive test case 1”, expect: func() { mockDoer.EXPECT().SaySomething(“spam”).Return(“bad”) }, args: args{num: 3}, wantErr: false, want: “bad”, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.expect() if got := user.SaySomething(tt.args.num); (got != tt.want) != tt.wantErr { fmt.Println(“gott :”, got) t.Errorf(“User.SaySomething() = %v, want %v”, got, tt.want) } }) } } 而单元测试的结果将是这样的: === RUN TestUser_SaySomething === RUN TestUser_SaySomething/Positive_test_case_1 — PASS: TestUser_SaySomething (0.00s) — PASS: TestUser_SaySomething/Positive_test_case_1 (0.00s) PASS ok github.com/tokopedia/go_learning/udemy/pzn/gomock_test/user 1.100s 到此这篇关于go语言中GoMock的实现的文章就介绍到这了,更多相关go语言 GoMock内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: 详解Golang中gomock的使用场景和方法 go语言中GoMock安装使用详解 用gomock进行mock测试的方法示例 使用Gomock进行单元测试的方法示例
  • 目录
    • 安装
    • 开始
    • 使用参数匹配器

    GoMock是一个Go框架。它与内置的测试包整合得很好,并在单元测试时提供了灵活性。正如我们所知,对具有外部资源(数据库、网络和文件)或依赖关系的代码进行单元测试总是很麻烦。

    为了使用GoMock,我们需要安装gomock包github.com/golang/mock/gomock和mockgen代码生成工具github.com/golang/mock/mockgen。使用这个命令行:

    go get github.com/golang/mock/gomock
    go get github.com/golang/mock/mockgen
    

    GoMock的使用遵循四个基本步骤:

    • 使用mockgen为你想模拟的接口生成一个模拟对象。
    • 在测试部分,创建一个gomock.Controller的实例,并把它传递给你的mock对象的构造函数以获得一个mock对象。
    • 在mock上调用EXPECT()来设置期望值和返回值。
    • 在模拟控制器上调用Finish()来断言模拟对象的期望。

    让我们创建一个这样的文件夹(本代码在 go1.16.15 版本下执行)

    gomock_test
    ├── doer
    │ └── doer.go
    ├── mocks
    │ └── mock_doer.go
    └── user
      ├── user.go
      └── user_test.go
    

    doer/doer.go

    package doer
    
    type Doer interface {
      DoSomething(int, string) error
      SaySomething(string) string
    }
    

    那么这里是我们在模拟Doer接口时要测试的代码。

    user/user.go

    package user
    
    import "gomock_test/doer"
    
    const (
    	filtered   = "OK"
    	unfiltered = "spam"
    	Nice       = "nice"
    	Bad        = "bad"
    )
    
    type User struct {
    	// struct while mocking the doer interface
    	Doer doer.Doer
    }
    
    // method Use using it
    func (u *User) Use() error {
    	return u.Doer.DoSomething(123, "Hello GoMock")
    }
    
    func (u *User) SaySomething(num int) string {
    	if num == 3 {
    		return u.Doer.SaySomething(unfiltered)
    	}
    	return u.Doer.SaySomething(filtered)
    }
    
    type Student struct{}
    
    func (s *Student) DoSomething(_ int, _ string) error {
    	panic("not implemented") // TODO: Implement
    }
    
    func (s *Student) SaySomething(kata string) string {
    	if kata == filtered {
    		return Nice
    	}
    	return Bad
    }
    

    我们将把Doer的模拟放在一个包mocks中。我们首先创建一个包含我们的模拟实现的目录mocks,然后在doer包上运行mockgen:

    mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer
    

    NOTE: 在执行这步的时候,会报错:Failed to format generated source code: mocks/mock_doer.go:5:15: expected ‘;’, found '.’ 这个时候,我们只需要将打印出来的代码复制到我们对应的文件中即可。

    当有大量的接口/包需要模拟时,为每个包和接口运行mockgen是一种乌托邦。为了缓解这个问题,可以将mockgen命令与go:generate放在一起。

    go:generate mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer
    

    我们必须自己创建目录模拟,因为GoMock不会为我们这样做,而是会以错误退出。

    • destination=…/mocks/mock_doer.go : 把生成的mocks放在这个路径下。
    • -package=mocks : 把生成的mocks放在这个包里
    • gomock_test/doer : 为这个包生成mocks。
    • Doer : 为这个接口生成mocks(必填),因为我们需要指定哪个接口来生成mocks。(如果需要的话,可以用逗号分隔的列表来指定多个接口。例如,Doer1, Doer2)

    因为我们对mockgen的调用在我们的项目中放置了一个文件mocks/mock_doer.go。这就是这样一个生成的mock实现的样子:

    // Code generated by MockGen. DO NOT EDIT.
    // Source: github.com/timliudream/go-test/gomock_test/doer (interfaces: Doer)
    
    // Package github.com/timliudream/go-test/gomock_test/mocks is a generated GoMock package.
    package mocks
    
    import (
    	gomock "github.com/golang/mock/gomock"
    	reflect "reflect"
    )
    
    // MockDoer is a mock of Doer interface.
    type MockDoer struct {
    	ctrl     *gomock.Controller
    	recorder *MockDoerMockRecorder
    }
    
    // MockDoerMockRecorder is the mock recorder for MockDoer.
    type MockDoerMockRecorder struct {
    	mock *MockDoer
    }
    
    // NewMockDoer creates a new mock instance.
    func NewMockDoer(ctrl *gomock.Controller) *MockDoer {
    	mock := &MockDoer{ctrl: ctrl}
    	mock.recorder = &MockDoerMockRecorder{mock}
    	return mock
    }
    
    // EXPECT returns an object that allows the caller to indicate expected use.
    func (m *MockDoer) EXPECT() *MockDoerMockRecorder {
    	return m.recorder
    }
    
    // DoSomething mocks base method.
    func (m *MockDoer) DoSomething(arg0 int, arg1 string) error {
    	m.ctrl.T.Helper()
    	ret := m.ctrl.Call(m, "DoSomething", arg0, arg1)
    	ret0, _ := ret[0].(error)
    	return ret0
    }
    
    // DoSomething indicates an expected call of DoSomething.
    func (mr *MockDoerMockRecorder) DoSomething(arg0, arg1 interface{}) *gomock.Call {
    	mr.mock.ctrl.T.Helper()
    	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DoSomething", reflect.TypeOf((*MockDoer)(nil).DoSomething), arg0, arg1)
    }
    
    // SaySomething mocks base method.
    func (m *MockDoer) SaySomething(arg0 string) string {
    	m.ctrl.T.Helper()
    	ret := m.ctrl.Call(m, "SaySomething", arg0)
    	ret0, _ := ret[0].(string)
    	return ret0
    }
    
    // SaySomething indicates an expected call of SaySomething.
    func (mr *MockDoerMockRecorder) SaySomething(arg0 interface{}) *gomock.Call {
    	mr.mock.ctrl.T.Helper()
    	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaySomething", reflect.TypeOf((*MockDoer)(nil).SaySomething), arg0)
    }
    

    接下来,我们在测试中定义一个模拟控制器。一个模拟控制器负责跟踪和断言其相关模拟对象的期望。我们可以通过传递一个*testing.T类型的值给它的构造函数来获得一个模拟控制器,并使用它来构造一个Doer接口的模拟对象。

    //core of gomock
    mockCtrl := gomock.NewController(t)
    //used to trigger final assertions. if its ignored, mocking assertions will never fail
    defer mockCtrl.Finish()
    // create a new mock object, passing the controller instance as parameter
    // for a newly created mock object it will accept any input and outpuite
    // need to define its behavior with the method expect
    mockDoer := mocks.NewMockDoer(mockCtrl)
    

    在GoMock中,一个参数可以被期望有一个固定的值,也可以被期望与一个谓词(称为匹配器)相匹配。匹配器用于表示被模拟方法的预期参数范围。下列匹配器在Gomock中被预先定义了:

    • gomock.Any() : 匹配任何值(任何类型)。
    • gomock.Eq(x) : 使用反射来匹配与x深度相等的值。
    • gomock.Nil() : 匹配nil

    user/user_test.go

    package user
    
    import (
    	"fmt"
    	"github.com/golang/mock/gomock"
    	"gomock_test/mocks"
    	"testing"
    )
    
    func TestUse(t *testing.T) {
    	//core of gomock
    	mockCtrl := gomock.NewController(t)
    	//used to trigger final assertions. if its ignored, mocking assertions will never fail
    	defer mockCtrl.Finish()
    	// create a new mock object, passing the controller instance as parameter
    	// for a newly created mock object it will accept any input and outpuite
    	// need to define its behavior with the method expect
    	mockDoer := mocks.NewMockDoer(mockCtrl)
    	testUser := &User{Doer: mockDoer}
    	//
    	// Expect Do to be called once with 123 and "Hello GoMock" as parameters, and return nil from the mocked call.
    	mockDoer.EXPECT().DoSomething(123, "Hello GoMock").Return(nil).Times(1)
    	fmt.Println(testUser.Use())
    	fmt.Println()
    }
    
    func TestUser_SaySomething(t *testing.T) {
    	mockCtrl := gomock.NewController(t)
    	defer mockCtrl.Finish()
    	mockDoer := mocks.NewMockDoer(mockCtrl)
    	user := User{
    		Doer: mockDoer,
    	}
    	type args struct {
    		num int
    	}
    	tests := []struct {
    		name    string
    		args    args
    		want    string
    		expect  func()
    		wantErr bool
    	}{
    		{
    			name: "Positive test case 1",
    			expect: func() {
    				mockDoer.EXPECT().SaySomething("spam").Return("bad")
    			},
    			args:    args{num: 3},
    			wantErr: false,
    			want:    "bad",
    		},
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			tt.expect()
    			if got := user.SaySomething(tt.args.num); (got != tt.want) != tt.wantErr {
    				fmt.Println("gott :", got)
    				t.Errorf("User.SaySomething() = %v, want %v", got, tt.want)
    			}
    		})
    	}
    }
    

    而单元测试的结果将是这样的:

    === RUN   TestUser_SaySomething
    === RUN   TestUser_SaySomething/Positive_test_case_1
    --- PASS: TestUser_SaySomething (0.00s)
        --- PASS: TestUser_SaySomething/Positive_test_case_1 (0.00s)
    PASS
    ok      github.com/tokopedia/go_learning/udemy/pzn/gomock_test/user     1.100s
    

    到此这篇关于go语言中GoMock的实现的文章就介绍到这了,更多相关go语言 GoMock内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • 详解Golang中gomock的使用场景和方法
    • go语言中GoMock安装使用详解
    • 用gomock进行mock测试的方法示例
    • 使用Gomock进行单元测试的方法示例

    站内搜索