Skip to content
Back to skills

Mockery Suite Tests

ASecurity

Use when writing Go tests that need test doubles or cover multiple cases - detect the repo's mocking approach (Mockery v3, gomock, hand-written testify mocks, fakes), table-driven cases, testify suites when the repo uses them, and real-Postgres tests for repository adapters

  • 109 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 19, 2026
ai-agentsgosqltestingapidatabase

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 2, 2026

npx -y skills add makifbaysal/tasktrooper --skill mockery-suite-tests --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mockery Suite Tests?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mockery Suite Tests
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/makifbaysal-mockery-suite-tests/badge)](https://www.skillsdirectory.com/skills/makifbaysal-mockery-suite-tests)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mockery-suite-tests
category: testing
description: Use when writing Go tests that need test doubles or cover multiple cases - detect the repo's mocking approach (Mockery v3, gomock, hand-written testify mocks, fakes), table-driven cases, testify suites when the repo uses them, and real-Postgres tests for repository adapters
tech_stack: Go
---
# Go Test Doubles & Table Tests

## Overview

Table-driven tests and real-Postgres repository-adapter tests are non-negotiable everywhere. How test doubles are produced is not: follow the repository's own convention (rule repo-conventions-win) rather than importing Mockery into a repo that hand-writes testify mocks or uses gomock.

**Core principle:** multi-case behaviour is a table; a repository adapter test runs against real Postgres, never a mocked driver; mocks exist only at port boundaries.

## Detect the repo's mocking approach first

```
ls .mockery.y*ml 2>/dev/null
grep -rlE 'Code generated by mockery|go.uber.org/mock' --include='*.go' . | head -3
```

- `.mockery.yml` or files headed "Code generated by mockery" → **Mockery v3**: regenerate, don't hand-edit (see below).
- `go.uber.org/mock` imports → **gomock**: regenerate with `mockgen` per the repo's `go:generate` directive, don't hand-edit.
- Neither, but a test file defines `type mockFoo struct` implementing a port → the repo **hand-writes testify mocks** (or fakes). Follow that style; do not introduce a generator the repo doesn't already have. TaskTrooper's own server does exactly this (`server/internal/port/mocks/llm_client.go`).

## Mockery v3 (when the repo already uses it)

- Mocks come from the port interfaces, generated by Mockery v3. Names and location come from `.mockery.yml`; v3's defaults are `Mock<Interface>` in `mocks_test.go` inside the source package, constructor `NewMock<Interface>(t)` — don't assume a different naming scheme without checking the config.
- Regenerate with the repo's own command (`mockery`, `go generate ./...`, or `make mocks`) — never hand-edit a generated mock.
- Install Mockery from a pinned release binary or `brew install mockery`; the project's own docs say not to `go install mockery@latest`.
- A v2 `.mockery.yaml` migrates with `mockery migrate`; don't hand-port the config.
- v3 generates an `expecter` API — use the typed `EXPECT()` methods, not raw `On("MethodName", ...)` string calls, so a renamed method breaks compilation instead of silently passing.

```go
repo := mocks.NewMockTaskRepository(t)        // generated constructor, auto-asserts on cleanup
repo.EXPECT().Create(mock.Anything, task).Return(nil).Once()
svc := NewTaskService(repo)
```

## Table-driven tests (every repo, every approach)

Every function with more than one case is a table. One row = one behavior with a descriptive name.

```go
func TestTaskService_Create(t *testing.T) {
    tests := []struct {
        name    string
        title   string
        setup   func(*mockTaskRepository)
        wantErr error
    }{
        {name: "valid title persists",
            title: "Report",
            setup: func(r *mockTaskRepository) { r.onCreate(nil) }},
        {name: "blank title rejected before DB",
            title: "  ",
            setup: func(r *mockTaskRepository) { /* no expectation: repo must NOT be called */ },
            wantErr: domain.ErrInvalidTitle},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            repo := newMockTaskRepository(t)
            tt.setup(repo)
            _, err := NewTaskService(repo).Create(t.Context(), tt.title)
            if !errors.Is(err, tt.wantErr) {
                t.Fatalf("got %v, want %v", err, tt.wantErr)
            }
        })
    }
}
```

Cover success, every error path, and edges (empty, not-found, conflict) as rows. A blank-title row that sets NO expectation proves the repo is never touched on invalid input. Use a testify suite (`suite.Suite` + `SetupTest`) only where the repo already uses one for shared fixtures — suites don't support `t.Parallel()`, so don't introduce one into a repo that parallelizes its tests.

## Repository adapter tests run against real Postgres

A repository adapter test (the thing implementing the port against pgx/`database/sql`) is never unit-tested against a mocked driver — it proves nothing about the actual SQL. Use the repo's own test-database harness, or `testcontainers-go` `modules/postgres` with the repo's migrations applied, and assert on rows actually read back.

## Modern Go testing

- `t.Context()` (Go ≥1.24) instead of `context.Background()` in tests — it's canceled on test cleanup.
- `testing/synctest` (Go ≥1.25) for deterministic tests of timers, timeouts and goroutine scheduling instead of real `time.Sleep`.
- `go test -race -count=1 ./...` for anything touching concurrency — run it, don't just suggest it.

## Rules

- Mock ONLY at port boundaries (repositories, external clients). Domain logic uses real domain values — no mocks.
- Assert on behavior and returned values, not on internal call ordering — over-specified expectations break on every refactor. Use `.Once()`/`.Times(n)` (or the gomock/hand-written equivalent) only where the count is the behavior under test.
- Keep the suite green and output pristine: a happy-path test that logs errors hides real failures.
- Never add a mocking library, generator or config file to a repository that doesn't already have one just to follow this skill — that is scope the reviewer bounces (rule repo-conventions-win).

## Common Mistakes

- Introducing Mockery into a repo that hand-writes mocks or uses gomock.
- Hand-editing a generated mock instead of regenerating it.
- Raw `.On("Create", ...)` string calls on a v3 expecter-generated mock.
- One giant test with `if`-branches per case → make it a table.
- Mocking a domain value object → construct the real one.
- A repository-adapter test that mocks the SQL driver instead of running against Postgres.

## Red Flags

- `type mock... struct` hand-written in a repo whose other tests use a generator.
- A test still compiles after an interface method was renamed → check whether it should have failed to.
- Rows share a mock instance and pass only in a specific order.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…