Painless Isolation: Mock and Fixtures in Go Tests

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

When Reality Gets in the Way: Why Do We Need Mocks and Fixtures in Go?

\n\n

Imagine you are testing a function that sends an email notification. Every test run sends a real email to the inbox. After ten tests, your inbox is full, and your colleagues curse the spam. Or worse: the function calls an external API that is paid or only works once a day.

\n\n

In the world of Go (or Golang, as it is often called), testing is not just a checkbox on a list. It is the foundation of reliability. But for tests to be fast and independent, we need to isolate the code from the outside world. This is where mock objects (mocks) and fixtures come into play.

\n\n

In this article, we will explore how to properly substitute dependencies (mock) and prepare data (fixtures) in Go. You will learn how to write tests that don't fail due to network issues, databases, or the time of day. Let's go!

\n\n

What are Mock and Fixture in the Context of Go?

\n\n

Mock Objects: Doubles for Complex Systems

\n\n

Mock objects are software "stubs" that simulate the behavior of real components. Instead of calling a real database or HTTP service, we substitute an object that returns predefined responses.

\n\n

In Go, mocks are especially important because the language encourages working with interfaces. If your code depends on an interface, you can easily replace the implementation with a test one.

\n\n

Fixtures: Setting the Stage

\n\n

Fixtures are data or state that must be set up before a test begins. For example, creating a temporary database, populating it with test records, or configuring the file system.

\n\n

Unlike mocks, which replace behavior, fixtures prepare the environment. They answer the question: "What needs to be ready for the test to start?"

\n\n

Writing Mocks in Go: From Simple to Complex

\n\n

The Simplest Way: Manual Interface Implementation

\n\n

Go does not require special libraries to create mocks. If you have an interface, you can write its test implementation manually. This is ideal for small projects.

\n\n

Let's say we have an interface for sending notifications:

\n\n
package notifier\n\ntype Notifier interface {\n    Send(to string, message string) error\n}\n\n// Real implementation (not for tests)\ntype EmailNotifier struct{}\n\nfunc (e *EmailNotifier) Send(to string, message string) error {\n    // Sending a real email\n    return nil\n}
\n\n

In the test file, we create a mock:

\n\n
package notifier\n\nimport "testing"\n\ntype MockNotifier struct {\n    Called bool\n    LastTo string\n    LastMsg string\n}\n\nfunc (m *MockNotifier) Send(to string, msg string) error {\n    m.Called = true\n    m.LastTo = to\n    m.LastMsg = msg\n    return nil\n}\n\nfunc TestSendAlert(t *testing.T) {\n    mock := &MockNotifier{}\n    // Use mock in a function that accepts Notifier\n    SendAlert(mock, "user@test.com", "Hello!")\n    \n    if !mock.Called {\n        t.Error("Send method was not called")\n    }\n    if mock.LastTo != "user@test.com" {\n        t.Errorf("Incorrect recipient: %s", mock.LastTo)\n    }\n}
\n\n

Pros: full control, no dependencies.
\nCons: a lot of boilerplate code for complex interfaces.

\n\n

Using testify/mock: Powerful and Convenient

\n\n

The testify library is the de facto standard for mocks in Go. It provides a flexible framework for creating mocks with argument and call count verification.

\n\n

Installation:

\n
go get github.com/stretchr/testify
\n\n

Example using testify/mock:

\n\n
package user\n\nimport (\n    "testing"\n    "github.com/stretchr/testify/mock"\n)\n\n// Define the interface\ntype UserRepository interface {\n    GetUser(id int) (string, error)\n}\n\n// Create the mock struct\ntype                

Blogs

Book Recommendations