mockery
SKILL.md
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
dirvalue. - 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-lintandmake check-buildto 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)registerst.Cleanup(func() { mock.AssertExpectations(t) })automatically — never callAssertExpectationsmanually..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
Weekly Installs
11
Repository
hiromaily/go-cr…o-walletGitHub Stars
125
First Seen
14 days ago
Security Audits
Installed on
gemini-cli11
opencode11
codebuddy11
github-copilot11
codex11
kimi-cli11