# Testing — google/wire Wire generates plain Go constructor calls, so tests work directly on the constructor layer — no container API to learn. ## Unit Tests: Plain Constructor Injection The generated code has no wire dependency. Test constructors directly: ```go func TestUserService_GetUser(t *testing.T) { mockStore := &MockUserStore{users: map[int64]*User{1: &User{ID: 1, Name: "Alice"}}} cache := newTestRedis(t) svc := service.NewUserService(mockStore, cache) u, err := svc.GetUser(context.Background(), 1) require.NoError(t, err) assert.Equal(t, "Alice", u.Name) } ``` Pass mocks directly as constructor arguments. No wire, no container, no file to generate. This is the idiomatic approach for unit tests. ## Test Injectors: Swapping Providers For integration or component tests where you want the full wired graph but with selected dependencies replaced, create a test-only injector in a `_test.go` file. ```go // app_test.go //go:build wireinject package main import ( "testing" "github.com/google/wire" ) // TestSet replaces real infra with in-memory fakes var TestSet = wire.NewSet( NewTestConfig, NewInMemoryUserStore, wire.Bind(new(repo.UserStore), new(*InMemoryUserStore)), NewTestRedis, ) func InitTestApp(t *testing.T) (*App, func(), error) { wire.Build(TestSet, service.ServiceSet, NewApp) return nil, nil, nil } ``` ```go // app_integration_test.go //go:build !wireinject // compiles when the wireinject tag is NOT set package main func TestApp_GetUser(t *testing.T) { app, cleanup, err := InitTestApp(t) require.NoError(t, err) defer cleanup() // test against the fully-wired app with fake dependencies u, err := app.GetUser(context.Background(), 1) require.NoError(t, err) assert.NotNil(t, u) } ``` Run `wire ./...` to generate `wire_gen.go` — the test injector is included because the `_test.go` file is compiled as part of the package during `go test`. **Key pattern from upstream best practices:** Prefer creating a test-only provider set over passing mocks as injector arguments (though both work). The set approach keeps the test injector composable. ## Passing Mocks as Injector Arguments An alternative to a test set: pass the mock directly as an injector parameter. Wire treats it as a pre-built provider. ```go //go:build wireinject func InitTestApp(store repo.UserStore) (*App, func(), error) { wire.Build(config.ConfigSet, service.ServiceSet, NewApp) return nil, nil, nil } // Test func TestApp(t *testing.T) { mock := &MockUserStore{} app, cleanup, err := InitTestApp(mock) require.NoError(t, err) defer cleanup() // ... } ``` Use this form when you only need to replace one or two dependencies and a full `TestSet` is overkill. ## CI: Detecting Stale `wire_gen.go` If `wire_gen.go` is not regenerated after a provider change, CI builds pass but the graph is wrong. Enforce freshness in CI: ```bash # Option 1: re-run wire and check for diffs wire ./... git diff --exit-code -- '**/wire_gen.go' ``` ```yaml # .github/workflows/ci.yml - name: Check wire_gen.go is up-to-date run: | go install github.com/google/wire/cmd/wire@v0.7.0 wire ./... git diff --exit-code -- '**/wire_gen.go' ``` ```bash # Option 2: use wire check (verifies graph without regenerating) wire check ./... ``` `wire check` exits non-zero if the graph is inconsistent but does **not** update `wire_gen.go`. Use it for a fast graph-validity check without modifying files. ## Testing Interface Bindings `wire.Bind` can be used in test sets to bind a fake to the same interface: ```go // Fake implements the same interface as the real provider type FakeMailer struct{ sent []string } func (f *FakeMailer) Send(to, body string) error { f.sent = append(f.sent, to); return nil } var TestMailerSet = wire.NewSet( NewFakeMailer, wire.Bind(new(notification.Mailer), new(*FakeMailer)), ) var TestSet = wire.NewSet( TestMailerSet, realServiceSet, // everything else is real ) ``` This keeps the test injector narrow — only the Mailer is faked; the rest of the graph is real. ## Table-Driven Tests Without Wire Wire is an initialization tool. Once the object graph is built, table-driven tests on individual services need no wire involvement: ```go func TestUserService(t *testing.T) { cases := []struct { name string id int64 users map[int64]*User want string err bool }{ {"found", 1, map[int64]*User{1: &User{Name: "Alice"}}, "Alice", false}, {"not found", 99, nil, "", true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { svc := service.NewUserService(&MockUserStore{users: tc.users}, nil) u, err := svc.GetUser(context.Background(), tc.id) if tc.err { require.Error(t, err); return } assert.Equal(t, tc.want, u.Name) }) } } ``` Wire has no role here — the injector was only needed to build the object graph in `main` (or in an integration test). Unit tests construct dependencies directly.