Mockery Mock Generation Workflow
When to Activate This Skill
-
A new interface is added to internal/application/ports/
-
A test file contains a manually-written mock/stub for a ports interface
-
A developer asks to add mock coverage for an existing ports interface
-
A test needs to replace On(...).Return(...) setup on a hand-rolled struct with generated mock expectations
Placement Convention
Mocks live in the infrastructure layer, beside the concrete implementations:
Interface package (application/ports/ ) Mock output directory (infrastructure/ )
ports/api/btc/
infrastructure/api/btc/mocks/
ports/api/eth/
infrastructure/api/eth/mocks/
ports/api/xrp/
infrastructure/api/xrp/mocks/
ports/file/
infrastructure/storage/file/transaction/mocks/
ports/repository/cold/
infrastructure/repository/cold/mocks/
ports/repository/watch/
infrastructure/repository/watch/mocks/
Pattern: ports/<layer>/<pkg>/ → infrastructure/<layer>/<pkg>/mocks/
Step-by-Step Workflow
-
Locate the interface — find the Go file in internal/application/ports/ that defines the interface.
-
Determine the target directory — use the placement convention table above to identify the dir value.
-
Add or update .mockery.yaml — append the interface name under its package entry (or add a new package entry if none exists). See the template below.
-
Run make mockery — regenerates all mocks in one pass. Never create or edit mock files manually.
-
Verify — run make go-lint and make check-build to confirm the generated code compiles cleanly.
-
Update tests — replace any manual stub usage with NewMock*(t)
- .EXPECT() builder calls.
.mockery.yaml Entry Template
Adding an interface to an existing package entry
packages: github.com/hiromaily/go-crypto-wallet/internal/application/ports/<layer>/<pkg>: config: dir: "internal/infrastructure/<layer>/<pkg>/mocks" pkgname: "mocks" interfaces: ExistingInterface: MyNewInterface: # ← append here
Adding a brand-new package entry
github.com/hiromaily/go-crypto-wallet/internal/application/ports/<layer>/<pkg>: config: dir: "internal/infrastructure/<layer>/<pkg>/mocks" pkgname: "mocks" interfaces: MyNewInterface:
The global settings (filename , structname , template , formatter ) are already configured in .mockery.yaml and apply automatically — do not override them per-package.
Generated File Conventions
Setting Value
Filename mock_<interface_name_snakecase>.go
Struct name Mock<InterfaceName>
Constructor NewMock<InterfaceName>(t)
Package mocks
Header // Code generated by mockery; DO NOT EDIT.
Test Usage — Before / After
Before (manual stub — do NOT write this)
// ❌ Manually-written stub in a test file type stubAccountRepo struct{}
func (s *stubAccountRepo) GetOneMaxID(accountType domainAccount.AccountType) (*domainKey.BTCAccountKey, error) { return &domainKey.BTCAccountKey{Index: 0}, nil }
func TestFoo(t *testing.T) { repo := &stubAccountRepo{} uc := NewUseCase(repo) // ... // Manual assertion: // (no automatic call-count verification) }
After (mockery-generated mock — always use this)
// ✅ Import the generated mocks package import coldmocks "github.com/hiromaily/go-crypto-wallet/internal/infrastructure/repository/cold/mocks"
func TestFoo(t *testing.T) { repo := coldmocks.NewMockBTCAccountKeyRepositorier(t) // registers cleanup automatically repo.EXPECT(). GetOneMaxID(domainAccount.AccountTypeDeposit). Return(&domainKey.BTCAccountKey{Index: 0}, nil)
uc := NewUseCase(repo)
// ...
// AssertExpectations is called automatically via t.Cleanup — do NOT call it manually
}
Key differences:
-
NewMock*(t) registers t.Cleanup(func() { mock.AssertExpectations(t) }) automatically — never call AssertExpectations manually.
-
.EXPECT().Method(args...).Return(values...) provides type-safe, call-count-verified expectations.
-
The generated mock handles all type assertions internally — no args.Get(0).(*Type) boilerplate.
Exceptions — Do NOT Add to .mockery.yaml
Type Reason
Ethereumer (ETH) DI-layer-only monolithic interface; not consumed by use cases
ETHTransactionSender
Type alias for TxSender ; mockery cannot generate mocks for type aliases — use TxSender mock instead
Use case interfaces (internal/application/usecase/*/interfaces.go ) These are not ports interfaces; simple struct stubs are acceptable for testing use case orchestration
Composed/aggregate interfaces (Bitcoiner , ETHKeygenSignClient , etc.) Leaf interface mocks satisfy all compositions — add only the leaf interfaces that use cases depend on
Compile-time conformance stubs in *_test.go under ports/
Intentional type-check helpers, not test doubles — leave them as-is
Verification Commands
make mockery # Regenerate all mocks after .mockery.yaml changes make go-lint # Required: zero lint errors make check-build # Required: full build succeeds make go-test # Recommended: run tests for affected packages
Related Files
-
.mockery.yaml — SSOT for all mock generation configuration
-
.claude/rules/internal/mockery.md — project rules enforcing this convention
-
.claude/rules/internal/infrastructure-layer.md — infrastructure layer rules
-
.claude/rules/internal/application-layer.md — application layer rules