Unit Tests
Unit tests should be the first test layer for application behavior.
Most service, validation, formatting, and workflow logic should be testable without starting HTTP, queue workers, scheduler, or external infrastructure.
Command
From your App root:
go test ./...Expected result: the package containing the service reports ok. To run only the Users service test from JSON API Route:
go test ./internal/users -run 'TestService(FindsUser|RejectsEmptyID)$'Expected result:
ok your/module/internal/usersService Tests
Test services directly:
package users
import (
"context"
"errors"
"testing"
)
func TestServiceFindsUser(t *testing.T) {
service := NewService()
user, err := service.Find(context.Background(), "42")
if err != nil {
t.Fatalf("find user: %v", err)
}
if user.ID != "42" {
t.Fatalf("user id = %q, want %q", user.ID, "42")
}
}
func TestServiceRejectsEmptyID(t *testing.T) {
service := NewService()
_, err := service.Find(context.Background(), "")
if !errors.Is(err, ErrUserNotFound) {
t.Fatalf("error = %v, want %v", err, ErrUserNotFound)
}
}This matches the service from JSON API Route. Use fakes, in-memory repositories, or local drivers when they make behavior clear. The scenario contains the complete file; this excerpt focuses on the assertion pattern.
What to Unit Test
Unit tests are a good fit for:
- service behavior
- request normalization helpers
- response mapping helpers
- repository logic with fakes
- job handler logic without a worker runtime
- event subscriber logic without distributed transport
- scheduler target methods
Avoid Full Runtime Boot
Do not start the full App when the behavior belongs to one service or function.
Full runtime tests are useful, but they are slower and should be reserved for boundaries that need runtime wiring.
Next Steps
- HTTP Tests covers controller and route behavior.
- Job and Queue Tests covers background work.
- Integration Tests covers backend behavior.