# Go Testing in 2026: Unit Tests, Mocks and Technical Interview Questions > Master Go testing with the standard library, table-driven tests, mocks, and the patterns interviewers expect. Practical examples with testify, gomock, and the testing package. - Published: 2026-08-24 - Updated: 2026-08-24 - Author: Anthony Fillion-Maillet - Tags: go, testing, unit-tests, mocks, interview - Reading time: 9 min --- Go testing relies on the standard `testing` package, which ships with every Go installation and requires no external dependencies. Unlike frameworks in other languages, Go's approach favors simplicity: test files live alongside production code, test functions follow a naming convention, and the `go test` command handles discovery and execution. > **What interviewers look for** > > Candidates who write table-driven tests, use interfaces for dependency injection, and understand when mocking adds value versus when it adds noise. ## Writing Unit Tests with the testing Package Every test file ends with `_test.go` and lives in the same package as the code it tests. Test functions start with `Test` followed by a capitalized name. The `*testing.T` parameter provides methods for reporting failures. ```go // calculator_test.go package calculator import "testing" func TestAdd(t *testing.T) { result := Add(2, 3) if result != 5 { t.Errorf("Add(2, 3) = %d; want 5", result) } } ``` Running `go test ./...` executes all tests in the current module. The `-v` flag shows individual test names and their pass/fail status. ## Table-Driven Tests: The Go Standard Table-driven tests reduce duplication and make adding new cases trivial. Each row in the table represents one scenario with inputs and expected outputs. This pattern appears in the [Go standard library](https://cs.opensource.google/go/go/+/refs/tags/go1.23.1:src/strings/strings_test.go) and in production codebases across the industry. ```go // validator_test.go package validator import "testing" func TestValidateEmail(t *testing.T) { tests := []struct { name string email string wantErr bool }{ {"valid email", "user@example.com", false}, {"missing at sign", "userexample.com", true}, {"empty string", "", true}, {"unicode local part", "user@例え.jp", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateEmail(tt.email) if (err != nil) != tt.wantErr { t.Errorf("ValidateEmail(%q) error = %v, wantErr %v", tt.email, err, tt.wantErr) } }) } } ``` The `t.Run` method creates subtests that run independently. Failed subtests report which specific case failed without stopping other cases. ## Dependency Injection Through Interfaces Go interfaces enable testing code that depends on external systems: databases, HTTP clients, file systems. Defining a minimal interface at the point of use allows swapping real implementations for test doubles. ```go // service.go package order // Repository defines the data access methods this service needs type Repository interface { FindByID(id string) (*Order, error) Save(order *Order) error } // Service handles order business logic type Service struct { repo Repository } // NewService creates a service with the given repository func NewService(repo Repository) *Service { return &Service{repo: repo} } // Process validates and saves an order func (s *Service) Process(order *Order) error { if order.Total <= 0 { return ErrInvalidTotal } return s.repo.Save(order) } ``` The `Service` accepts any type satisfying `Repository`. Production code passes a database-backed implementation. Tests pass a mock or stub. ## Mocking with gomock and mockgen The [gomock](https://github.com/uber-go/mock) package generates mock implementations from interface definitions. The `mockgen` command reads source files and produces mock structs that record calls and return configured values. ```bash # Generate mocks for the Repository interface mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocks ``` Using the generated mock in tests: ```go // service_test.go package order import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "yourmodule/order/mocks" ) func TestService_Process_SavesValidOrder(t *testing.T) { ctrl := gomock.NewController(t) mockRepo := mocks.NewMockRepository(ctrl) order := &Order{ID: "123", Total: 99.99} // Expect Save to be called once with this order, return nil mockRepo.EXPECT(). Save(order). Return(nil). Times(1) svc := NewService(mockRepo) err := svc.Process(order) assert.NoError(t, err) } func TestService_Process_RejectsZeroTotal(t *testing.T) { ctrl := gomock.NewController(t) mockRepo := mocks.NewMockRepository(ctrl) // No calls expected to repo since validation fails first svc := NewService(mockRepo) err := svc.Process(&Order{ID: "456", Total: 0}) assert.ErrorIs(t, err, ErrInvalidTotal) } ``` The mock verifies that `Save` was called exactly once with the expected argument. If the code under test never calls `Save`, or calls it with different arguments, the test fails. ## Using testify for Assertions and Suites [testify](https://github.com/stretchr/testify) provides assertion functions that produce clearer failure messages than manual if-checks. The `assert` package continues execution after failures. The `require` package stops the test immediately. ```go // user_test.go package user import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParseUser(t *testing.T) { input := `{"id": "u1", "name": "Alice"}` user, err := ParseUser(input) require.NoError(t, err, "parsing should not fail") assert.Equal(t, "u1", user.ID) assert.Equal(t, "Alice", user.Name) assert.Empty(t, user.Email, "email should default to empty") } ``` The `require.NoError` call stops the test if parsing fails, preventing nil pointer panics on subsequent assertions. ## Testing HTTP Handlers with httptest The `net/http/httptest` package provides `ResponseRecorder` for capturing handler output and `Server` for running a local test server. Most unit tests use `ResponseRecorder` to avoid network overhead. ```go // handler_test.go package api import ( "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) func TestHealthHandler(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/health", nil) rec := httptest.NewRecorder() HealthHandler(rec, req) assert.Equal(t, http.StatusOK, rec.Code) assert.JSONEq(t, `{"status": "ok"}`, rec.Body.String()) } ``` For handlers that call external services, inject a mock client or use `httptest.NewServer` to create a fake backend. ## Common Interview Questions on Go Testing Interviewers assess testing knowledge through questions that reveal experience with real codebases. Here are patterns that distinguish prepared candidates: **"How would you test a function that calls an external API?"** Define an interface for the HTTP client, inject it into the function or struct, and provide a mock that returns predetermined responses. This avoids network calls and makes tests deterministic. **"When would you use a stub versus a mock?"** Stubs return canned responses without verifying how they were called. Mocks verify that specific methods were called with specific arguments. Use stubs when the return value matters more than the interaction. Use mocks when verifying the interaction is the point of the test. **"What are table-driven tests and why does Go favor them?"** Table-driven tests define test cases as data in a slice of structs. They reduce boilerplate, make adding cases trivial, and produce readable test output with `t.Run`. The pattern aligns with Go's preference for explicit, repetitive code over clever abstractions. **"How do you handle test fixtures or setup that multiple tests share?"** Use `TestMain` for package-level setup and teardown. Use helper functions or test suites (from testify) for shared setup across related tests. Avoid global state that creates coupling between tests. Preparing for a Go interview? The [Go testing module](/technologies/go/interview-questions/testing) covers these patterns in depth with practice questions. ## Running Tests with Coverage and Race Detection The `go test` command accepts flags that reveal untested code paths and concurrency bugs. ```bash # Run tests with coverage report go test -cover ./... # Generate HTML coverage visualization go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out -o coverage.html # Detect race conditions (slower, but catches real bugs) go test -race ./... ``` The race detector instruments memory accesses and flags concurrent reads and writes to the same variable. Running tests with `-race` in CI catches bugs that only manifest under specific timing. ## Organizing Tests in Large Codebases As projects grow, test organization affects maintainability: - **Same package tests** (`package foo`) access unexported functions and fields. Use for unit tests that verify internal behavior. - **External package tests** (`package foo_test`) import the package like external code. Use for integration tests and to verify the public API. - **Testdata directories** store fixtures like JSON files, golden outputs, or seed databases. Go ignores these during builds. - **Build tags** separate unit tests from integration tests that require external systems. Run `go test -tags=integration ./...` to include them. For more on Go best practices beyond testing, the [concurrency patterns article](/blog/go/go-concurrency-goroutines-channels) covers goroutines and channels. ## What to Remember About Go Testing - The `testing` package ships with Go and requires no external dependencies - Table-driven tests reduce duplication and are the expected pattern in interviews - Interfaces enable dependency injection; define them at the point of use, not at the implementation - `gomock` generates mocks from interfaces; `testify` provides cleaner assertions - `httptest.ResponseRecorder` captures handler output without network overhead - The `-race` flag detects concurrency bugs that unit tests otherwise miss - Coverage metrics guide attention but do not guarantee correctness --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/en/blog/go/go-testing-2026-unit-tests-mocks-interview