Backend Development 18 min read

Resolving MySQL Dependencies in Go Unit Tests with Fake and Mock Objects

This article explains how to write Go unit tests for HTTP handlers that depend on MySQL by using fake implementations and the gomock library to replace the store layer, demonstrating both fake object and mock approaches, code examples, and best practices for isolating external dependencies.

Go Programming World
Go Programming World
Go Programming World
Resolving MySQL Dependencies in Go Unit Tests with Fake and Mock Objects

When writing unit tests for Go code that relies on external services such as a MySQL database, the external dependency must be replaced with a test double. This guide shows how to do that for an HTTP service that provides POST /users and GET /users/:id endpoints.

HTTP Service Example

package main

import (
    "encoding/json"
    "fmt"
    "gorm.io/gorm"
    "io"
    "net/http"
    "strconv"
    "github.com/julienschmidt/httprouter"
    "github.com/jianghushinian/blog-go-example/test/mysql/store"
)

func NewUserHandler(db *gorm.DB) *UserHandler { return &UserHandler{store: store.NewUserStore(db)} }

type UserHandler struct { store store.UserStore }

func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { /* ... */ }
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { /* ... */ }

func setupRouter(handler *UserHandler) *httprouter.Router { /* ... */ }

func main() {
    mysqlDB, _ := store.NewMySQLDB("localhost", "3306", "user", "password", "test")
    handler := NewUserHandler(mysqlDB)
    router := setupRouter(handler)
    _ = http.ListenAndServe(":8000", router)
}

The UserHandler depends on the store.UserStore interface, which is defined in store/store.go and implemented by userStore that uses GORM to talk to MySQL.

Fake Testing

A simple way to break the MySQL dependency is to provide a fake implementation of store.UserStore :

type fakeUserStore struct{}

func (f *fakeUserStore) Create(user *store.User) error { return nil }
func (f *fakeUserStore) Get(id int) (*store.User, error) { return &store.User{ID: id, Name: "test"}, nil }

Using the fake, the handler can be instantiated as:

handler := &UserHandler{store: &fakeUserStore{}}

A unit test for GetUser then becomes a pure HTTP test without any database access.

Mock Testing with gomock

For larger projects a manually written fake becomes cumbersome. The gomock library can generate a mock automatically:

$ go get go.uber.org/mock/gomock@latest
$ go install go.uber.org/mock/mockgen@latest
$ mockgen -source store/store.go -destination store/mocks/gomock.go -package mocks

The generated MockUserStore implements the same interface. In a test you create a controller, set expectations, and inject the mock:

func TestUserHandler_GetUser_by_mock(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()
    mockUserStore := mocks.NewMockUserStore(ctrl)
    mockUserStore.EXPECT().Get(gomock.Eq(2)).Return(&store.User{ID: 2, Name: "user2"}, nil)
    handler := &UserHandler{store: mockUserStore}
    router := setupRouter(handler)
    w := httptest.NewRecorder()
    req := httptest.NewRequest("GET", "/users/2", nil)
    router.ServeHTTP(w, req)
    assert.Equal(t, 200, w.Code)
    assert.Equal(t, `{"id":2,"name":"user2"}`, w.Body.String())
}

The mock lets you verify that Get is called with the expected argument and to return different results for different scenarios, such as errors.

Additional gomock Features

gomock provides argument matchers ( Any() , Eq() , Not() , Nil() , Len() ), call count constraints ( Times() , MaxTimes() , AnyTimes() ) and ordering ( After() ), making it flexible for complex interfaces.

Summary

By defining the data‑access layer as an interface ( store.UserStore ) the HTTP handler can be tested without a real MySQL instance. A hand‑written fake is quick for simple cases, while gomock automates mock generation and provides powerful verification capabilities, enabling reliable unit tests for Go backend services.

Gounit testingMySQLMockdependency injectionGomockFake
Go Programming World
Written by

Go Programming World

Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.