GoMock Testing Guide
Writing tests, especially when using mocking libraries like GoMock, involves several steps and
concepts. I'll break down the example provided and explain each part in detail.
Step-by-Step Guide to Writing Tests with GoMock
1. Understand the Code Being Tested
Before writing the test, you should thoroughly understand the function or method you are testing. In
this case, it's the GetReport method in a service related to user reports.
2. Define the Test Function
Every test function in Go starts with Test and is followed by a descriptive name. For example,
TestGetReport.
3. Set Up the Test Environment
- t.Parallel(): This enables parallel execution of the test, which can speed up the test suite.
- Testcase Struct: Define a struct to hold the arguments and expected results for each test case. For
example:
type getReportArgs struct {
ctx context.Context
in *input.UserReportGetter
type getReportTestcase struct {
prepare func(f *PrepareGetReportFields)
args getReportArgs
expected *output.UserReportGetter
wantErr bool
4. Mocking Dependencies with GoMock
- Create Mocks: Use GoMock to create mock objects for the dependencies of the function being
tested.
ctrl := gomock.NewController(t)
mockBinder := mock_gateway.NewMockBinder(ctrl)
mockTransactor := mock_gateway.NewMockTransactor(ctrl)
mockReportQueriesGateway := mock_gateway.NewMockReportQueriesGateway(ctrl)
mockPresignedUrlGenerator := mock_service.NewMockPresignedUrlGenerator(ctrl)
- Define Prepare Function: This function sets up the expected behavior and return values for the
mocks.
prepare: func(f *PrepareGetReportFields) {
f.ctx = owner.WithConstruction(f.ctx, &existedConstruction)
f.mockBinder.EXPECT().Bind(gomock.Any()).Return(f.ctx).Times(1)
f.mockReportQueriesGateway.EXPECT().GetByIDAndConstructionID(f.ctx, existedConstruction.ID,
owner.ReportID("report-id-001"), nil).Return(&owner.Report{/*...*/}, nil).Times(1)
f.mockPresignedUrlGenerator.EXPECT().GenPairsFromReportPhotosAndReportDocuments(gomoc
k.Any(), /*...*/).Return(/*...*/).Times(1)
5. Run the Test Cases
- Loop Through Test Cases: Use a loop to iterate over each test case, applying the prepare function
and then calling the method under test.
for name, tt := range testTables {
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
mockBinder := mock_gateway.NewMockBinder(ctrl)
mockTransactor := mock_gateway.NewMockTransactor(ctrl)
mockReportQueriesGateway := mock_gateway.NewMockReportQueriesGateway(ctrl)
mockPresignedUrlGenerator := mock_service.NewMockPresignedUrlGenerator(ctrl)
reportGetterService := NewReportGetter(mockBinder, mockTransactor,
mockReportQueriesGateway, mockPresignedUrlGenerator)
f := &PrepareGetReportFields{ctx: tt.args.ctx, mockBinder: mockBinder, mockTransactor:
mockTransactor, mockReportQueriesGateway: mockReportQueriesGateway,
mockPresignedUrlGenerator: mockPresignedUrlGenerator}
if tt.prepare != nil {
tt.prepare(f)
realResult, err := reportGetterService.GetReport(f.ctx, tt.args.in)
if (err != nil) != tt.wantErr {
t.Errorf("GetReport() error = %v, wantErr %v", err, tt.wantErr)
if !reflect.DeepEqual(realResult, tt.expected) {
t.Errorf("GetReport() realResult = %v is not equal to expected %v", realResult, tt.expected)
})
Key Concepts and Terms
- Mock: A mock is an object that simulates the behavior of a real object. It allows you to define
expected method calls and return values.
- GoMock: A mocking framework for Go, which helps in creating mock objects and setting
expectations on their behavior.
- gomock.Controller: Manages the lifecycle of mock objects. It's passed to every mock object and
ensures proper cleanup.
- EXPECT(): Used to set up expected calls on the mock object and define their return values or
behavior.
- Times(n int): Specifies how many times the expected call should occur.
- gomock.Any(): A matcher that matches any value of the expected type.
Example Test Case Breakdown
- Successful Report Retrieval:
- Sets up the context and mock expectations.
- Defines the arguments for the method call.
- Checks that the method returns the expected result without error.
- Error Scenarios:
- Sets up different scenarios like database errors or not found errors.
- Ensures the method returns appropriate errors for these scenarios.