--- 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///` → `infrastructure///mocks/` ## Step-by-Step Workflow 1. **Locate the interface** — find the Go file in `internal/application/ports/` that defines the interface. 2. **Determine the target directory** — use the placement convention table above to identify the `dir` value. 3. **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. 4. **Run `make mockery`** — regenerates all mocks in one pass. Never create or edit mock files manually. 5. **Verify** — run `make go-lint` and `make check-build` to confirm the generated code compiles cleanly. 6. **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 ```yaml packages: github.com/hiromaily/go-crypto-wallet/internal/application/ports//: config: dir: "internal/infrastructure///mocks" pkgname: "mocks" interfaces: ExistingInterface: MyNewInterface: # ← append here ``` ### Adding a brand-new package entry ```yaml github.com/hiromaily/go-crypto-wallet/internal/application/ports//: config: dir: "internal/infrastructure///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_.go` | | Struct name | `Mock` | | Constructor | `NewMock(t)` | | Package | `mocks` | | Header | `// Code generated by mockery; DO NOT EDIT.` | ## Test Usage — Before / After ### Before (manual stub — do NOT write this) ```go // ❌ 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) ```go // ✅ 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 ```bash 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