An example go library that uses go.uber.org/mock
to easily
mock an external service for unit testing.
An important Application
method CoolAlgorithm
needs to be unit tested.
go-mock/application/CoolAlgorithm.go
Line 4 in 94b8191
func (application *Application) CoolAlgorithm(input int) int { |
CoolAlgorithm
relies on an external calculator
service and for whatever reason, that external
service cannot be used during unit testing.
To remedy this, we first define the Calculatorer
interface:
go-mock/calculator/Calculatorer.go
Lines 4 to 13 in 94b8191
type Calculatorer interface { | |
// Add returns the sum of two ints. | |
Add(int, int) int | |
// Subtract returns the difference of two ints. | |
Subtract(int, int) int | |
// Multiply returns the product of two ints. | |
Multiply(int, int) int | |
// Divide returns the quotient of two ints. | |
Divide(int, int) int | |
} |
In order to mock this interface, we first have to install mockgen
:
go install go.uber.org/mock/mockgen@latest
We then use mockgen
to generate a mock client to use in our unit test:
mockgen -source=./calculator/Calculatorer.go -destination=./mock_calculator/Client.go
go-mock/mock_calculator/Client.go
Lines 1 to 94 in 94b8191
// Code generated by MockGen. DO NOT EDIT. | |
// Source: ./calculator/Calculatorer.go | |
// | |
// Generated by this command: | |
// | |
// mockgen -source=./calculator/Calculatorer.go -destination=./mock_calculator/Client.go | |
// | |
// Package mock_calculator is a generated GoMock package. | |
package mock_calculator | |
import ( | |
reflect "reflect" | |
gomock "go.uber.org/mock/gomock" | |
) | |
// MockCalculatorer is a mock of Calculatorer interface. | |
type MockCalculatorer struct { | |
ctrl *gomock.Controller | |
recorder *MockCalculatorerMockRecorder | |
} | |
// MockCalculatorerMockRecorder is the mock recorder for MockCalculatorer. | |
type MockCalculatorerMockRecorder struct { | |
mock *MockCalculatorer | |
} | |
// NewMockCalculatorer creates a new mock instance. | |
func NewMockCalculatorer(ctrl *gomock.Controller) *MockCalculatorer { | |
mock := &MockCalculatorer{ctrl: ctrl} | |
mock.recorder = &MockCalculatorerMockRecorder{mock} | |
return mock | |
} | |
// EXPECT returns an object that allows the caller to indicate expected use. | |
func (m *MockCalculatorer) EXPECT() *MockCalculatorerMockRecorder { | |
return m.recorder | |
} | |
// Add mocks base method. | |
func (m *MockCalculatorer) Add(arg0, arg1 int) int { | |
m.ctrl.T.Helper() | |
ret := m.ctrl.Call(m, "Add", arg0, arg1) | |
ret0, _ := ret[0].(int) | |
return ret0 | |
} | |
// Add indicates an expected call of Add. | |
func (mr *MockCalculatorerMockRecorder) Add(arg0, arg1 any) *gomock.Call { | |
mr.mock.ctrl.T.Helper() | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockCalculatorer)(nil).Add), arg0, arg1) | |
} | |
// Divide mocks base method. | |
func (m *MockCalculatorer) Divide(arg0, arg1 int) int { | |
m.ctrl.T.Helper() | |
ret := m.ctrl.Call(m, "Divide", arg0, arg1) | |
ret0, _ := ret[0].(int) | |
return ret0 | |
} | |
// Divide indicates an expected call of Divide. | |
func (mr *MockCalculatorerMockRecorder) Divide(arg0, arg1 any) *gomock.Call { | |
mr.mock.ctrl.T.Helper() | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Divide", reflect.TypeOf((*MockCalculatorer)(nil).Divide), arg0, arg1) | |
} | |
// Multiply mocks base method. | |
func (m *MockCalculatorer) Multiply(arg0, arg1 int) int { | |
m.ctrl.T.Helper() | |
ret := m.ctrl.Call(m, "Multiply", arg0, arg1) | |
ret0, _ := ret[0].(int) | |
return ret0 | |
} | |
// Multiply indicates an expected call of Multiply. | |
func (mr *MockCalculatorerMockRecorder) Multiply(arg0, arg1 any) *gomock.Call { | |
mr.mock.ctrl.T.Helper() | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Multiply", reflect.TypeOf((*MockCalculatorer)(nil).Multiply), arg0, arg1) | |
} | |
// Subtract mocks base method. | |
func (m *MockCalculatorer) Subtract(arg0, arg1 int) int { | |
m.ctrl.T.Helper() | |
ret := m.ctrl.Call(m, "Subtract", arg0, arg1) | |
ret0, _ := ret[0].(int) | |
return ret0 | |
} | |
// Subtract indicates an expected call of Subtract. | |
func (mr *MockCalculatorerMockRecorder) Subtract(arg0, arg1 any) *gomock.Call { | |
mr.mock.ctrl.T.Helper() | |
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subtract", reflect.TypeOf((*MockCalculatorer)(nil).Subtract), arg0, arg1) | |
} |
Finally, we construct an Application
instance that uses our mocked client. We set expectations and
return values for the methods that will be called during the CoolAlgorithm
unit test:
go-mock/application/CoolAlgorithm_test.go
Lines 11 to 44 in 94b8191
func TestCoolAlgorithm(t *testing.T) { | |
ctrl := gomock.NewController(t) | |
mockCalculator := mock_calculator.NewMockCalculatorer(ctrl) | |
application := application.Application{Calculator: mockCalculator} | |
number := 100 | |
mockCalculator. | |
EXPECT(). | |
Add(number, 0). | |
Times(1). | |
Return(number) | |
mockCalculator. | |
EXPECT(). | |
Subtract(number, 0). | |
Times(1). | |
Return(number) | |
mockCalculator. | |
EXPECT(). | |
Multiply(number, 1). | |
Times(1). | |
Return(number) | |
mockCalculator. | |
EXPECT(). | |
Divide(number, 1). | |
Times(1). | |
Return(number) | |
if application.CoolAlgorithm(number) != number { | |
t.Fail() | |
} | |
} |