Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
78: Mocking Interfaces for Tests
You've probably heard that to do "real" mocking in Go, you need a heavy-duty framework like GoMock or Testify. There's a common belief that unless you're using a tool that auto-generates mocks, your tests are just "amateur hour" or too manual to maintain. I'll be honest: I see this all the time with developers coming from Java or C#. They want a magic @Mock annotation that handles everything for them.
The Myth: Mocking Frameworks are Mandatory for Interface Tests
The problem with relying on a framework too early is that it obscures what's actually happening. If you try to mock a concrete struct—say, a DatabaseConnection struct—you'll quickly find that Go simply doesn't let you "override" methods on a struct the way you can in some other languages. You can't just tell a struct to behave differently during a test.
// This is what people TRY to do, but it doesn't work
type EmailSender struct {}
func (e *EmailSender) Send(msg string) error {
// actually sends an email... we don't want this in tests!
return nil
}
// You can't "intercept" this call without an interface.
If you try to force this, you'll end up with brittle tests or a codebase cluttered with "test-only" flags inside your production logic. That's a recipe for a nightmare.
The Reality: Interfaces are the Mock
In Go, mocking isn't a "feature" provided by a library; it's a natural consequence of how interfaces work. If your logic depends on an interface rather than a concrete type, you can pass in anything that satisfies that interface. I personally prefer "manual mocks" for 90% of my work because they are explicit and don't require a code-generation step every time I add a method.
Let's look at a real-world scenario: a PaymentProcessor that needs to talk to an external API (like Stripe or PayPal). We don't want to hit the actual API every time we run go test.
type PaymentGateway interface {
Charge(amount int) error
}
type CheckoutService struct {
Gateway PaymentGateway
}
func (s *CheckoutService) CompletePurchase(amount int) error {
if amount <= 0 {
return errors.New("invalid amount")
}
return s.Gateway.Charge(amount)
}
Now, instead of reaching for a library, we just build a small struct in our _test.go file. Here is the trick I use: I add a function field to the mock struct. This allows me to define the behavior per test case rather than creating five different mock structs for five different scenarios.
type MockGateway struct {
ChargeFunc func(amount int) error
}
// This makes MockGateway satisfy the PaymentGateway interface
func (m *MockGateway) Charge(amount int) error {
return m.ChargeFunc(amount)
}
func TestCompletePurchase_Success(t *testing.T) {
mock := &MockGateway{
ChargeFunc: func(amount int) error {
return nil // Simulate success
},
}
service := CheckoutService{Gateway: mock}
err := service.CompletePurchase(100)
if err != nil {
t.Errorf("Expected success, got %v", err)
}
}
func TestCompletePurchase_ApiFailure(t *testing.T) {
mock := &MockGateway{
ChargeFunc: func(amount int) error {
return errors.New("api timeout") // Simulate failure
},
}
service := CheckoutService{Gateway: mock}
err := service.CompletePurchase(100)
if err == nil || err.Error() != "api timeout" {
t.Errorf("Expected api timeout error, got %v", err)
}
}
See what happened there? By using a function field (ChargeFunc), we've created a flexible, lightweight mock without adding a single external dependency to our go.mod. It's type-safe, it's fast, and any engineer reading your code can see exactly what the mock is doing without having to learn a specific framework's DSL (Domain Specific Language).
📋 Practical Task
Simulating a Flaky External Weather API
You are building a WeatherAlertService that checks the temperature via an external API and returns a warning if it's too cold. You need to test how your service handles a "Service Unavailable" error from the API without actually making network calls.
Your Task:
- Define a
WeatherClientinterface with a methodGetTemperature(city string) (int, error). - Create a
WeatherAlertServicestruct that takes aWeatherClientand has a methodIsFreezing(city string) (bool, error). This method should returntrueif the temperature is 0 or below, andfalseotherwise. - In your test file, create a
MockWeatherClientusing the function-field pattern. - Write a test case
TestIsFreezing_ApiErrorthat forces the mock to return aerrors.New("503 Service Unavailable")and asserts that theWeatherAlertServicecorrectly bubbles that error up.
There are no comments for now.