Middle · Architecture and systems
A mock passes while the real adapter breaks
A mocked repository test is green, but production code uses the original DB instead of the passed transaction. What did the test fail to prove?
Try explaining this aloud before opening the answer. State your assumptions and trace a boundary case.
package main
import (
"context"
"fmt"
)
type Order struct{ ID string }
// The consumer needs this capability, not every method of a database client.
type OrderReader interface {
Find(context.Context, string) (Order, error)
}
type Memory struct{ orders map[string]Order }
func (m Memory) Find(_ context.Context, id string) (Order, error) {
value, ok := m.orders[id]
if !ok {
return Order{}, fmt.Errorf("order absent")
}
return value, nil
}
func describe(ctx context.Context, reader OrderReader) {
o, err := reader.Find(ctx, "7")
fmt.Println(o.ID, err)
}
func main() { describe(context.Background(), Memory{map[string]Order{"7": {ID: "7"}}}) }Reveal a strong answer
A repository with packages named controllers, managers and utils can still have no useful boundaries. Ask which behavior changes together and which caller is allowed to depend on it. Go import cycles are prohibited, but a cycle-free graph can still expose every storage struct and force unrelated features to change together. Test the public behavior of a capability, not its current arrangement of private helper calls.
Return domain values rather than leaking driver-specific result types across every layer. Export the smallest useful API, document error and ownership contracts, and use external package tests where appropriate to exercise what users can actually call. Use integration tests for serialization, SQL and network semantics that a fake cannot reproduce. The reader contract is declared near its consumer and contains one capability. The adapter satisfies it implicitly. In a real module, put private implementation under internal where the import rules fit the intended boundary. The tiny memory implementation is a test seam, not proof that a database adapter follows the same transaction and cancellation behavior.
Creating an interface for every struct increases maintenance without adding a boundary. A mock that asserts a private call order can reject a harmless refactor and miss a broken transaction. Avoid a shared models package that makes every subsystem depend on every field. Test contract cases against both a fake and the real adapter when substitutability matters.
Common trap
A mock’s matching method names do not prove substitutability of the real transport or database semantics.
Try it
Split public use-case behavior tests from adapter integration tests and identify the contract cases both implementations must satisfy.
Worked solution
A fake can prove the use case calls its declared capability under selected inputs, but it does not reproduce database transaction membership, isolation or cancellation. Keep the consumer interface narrow, test observable behavior and run real integration cases for serialization, transaction ownership and unknown outcomes. Avoid tests whose only assertion is a private helper call sequence; they can be brittle while missing the actual invariant.
Change the constraint
Which errors and ownership promises belong in the interface documentation?
How did you do?
Self-assessment, not a compiler pass or interview score. Explain the reasoning unaided before marking understood.
Not marked. Progress stays on this browser, separate from course completion.
Primary references
Checked 2026-10-28. Use documentation for your pinned runtime, driver and tool version.