| name | mockery |
| description | Mock generation workflow for go-crypto-wallet. Activate whenever a developer asks to generate a mock for a new interface, add test coverage that requires a mock, or replace a manually-written test stub for a ports interface. Claude MUST use `make mockery` — never write mock struct code by hand.
|
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:
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)
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)
}
After (mockery-generated mock — always use this)
import coldmocks "github.com/hiromaily/go-crypto-wallet/internal/infrastructure/repository/cold/mocks"
func TestFoo(t *testing.T) {
repo := coldmocks.NewMockBTCAccountKeyRepositorier(t)
repo.EXPECT().
GetOneMaxID(domainAccount.AccountTypeDeposit).
Return(&domainKey.BTCAccountKey{Index: 0}, nil)
uc := NewUseCase(repo)
}
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
make go-lint
make check-build
make go-test
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