Communityライティング&編集github.com

DanielCardonaRojas/swift-mocking

A compact swift mocking library powered by macros and parameter packs.

swift-mocking とは?

swift-mocking is a Claude Code agent skill that a compact swift mocking library powered by macros and parameter packs.

対応~Claude Code~Codex CLI~Cursor
npx skills add DanielCardonaRojas/swift-mocking

Installed? Explore more ライティング&編集 skills: steipete/notion, affaan-m/seo, affaan-m/brand-voice · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

SwiftMocking

Mocking for Swift protocols: @Mockable generates mock classes; when(...) stubs, verify(...) asserts calls. When the macro can't generate a mock — most notably protocol inheritance (protocol B: A — the macro drops inherited requirements and the mock fails to conform) — hand-write the mock following the exact generated-code shape.

When to use what

SituationRead
Protocol has no inheritance; need a mock@Mockable protocol P {...} → use PMock()
Protocol inherits another protocol with membersmanual-mocking.md (macro cannot do this)
Mocking without any protocol (closure/TCA dependencies)usage.md — Spy + adapt
Stubbing/verifying settable properties or subscripts ({ get set })usage.md — Properties & subscripts; hand-written shape in manual-mocking.md
Need mock source without the macro (plugin unavailable, codegen, review)mockable CLI (below) when available; hand-writing per manual-mocking.md is always valid
@Sendable/Swift 6 concurrency errors when stubbingsendable.md
Stubbing / matching / verifying API referenceusage.md
Exact signature of a public API (overloads, constraints, defaults)references/interface/ (below)

API interface

Machine-generated from the compiled modules — the authoritative signature reference. Check these before guessing at an overload or a generic constraint:

  • Core library: references/interface/SwiftMocking.swiftinterface
  • XCTest/swift-testing helpers: references/interface/SwiftMockingTestSupport.swiftinterface

One caveat: Sendable is a marker protocol and the compiler elides it from where clauses in emitted interfaces — thenThrow<E: Error & Sendable> prints as where E : Error. The constraint is still enforced. For Sendable questions trust sendable.md, not these files.

Deterministic generation: the mockable CLI

When a mock must exist as written source (macro plugin unavailable, codegen pipeline, review), the mockable CLI is the fastest exact path. Hand-writing per manual-mocking.md is equally correct and needs nothing; use the CLI when it's available, hand-write when it isn't or when you're already customizing.

echo 'protocol P { func price(_ item: String) throws -> Int }' | mockable

Invoke as mockable when it is on PATH; otherwise .build/release/mockable inside a swift-mocking checkout (build once with swift build -c release --product mockable, then optionally copy the binary onto PATH).

  • Options ride the input: @Mockable([.suffixMock]) protocol P {...} on stdin.
  • Default output keeps the macro's #if DEBUG wrapper; pass --no-debug-wrap when pasting into a test target (DEBUG is per build configuration — a wrapped mock vanishes under swift test -c release).
  • A stderr warning about inherited requirements means the output will not conform — hand-write per manual-mocking.md instead.
  • Output keeps the macro's zero-arg/property-getter shape: when(...) silently stubs a disconnected spy, and verify(...) reports zero calls for those members — apply the pinned-spy fix from manual-mocking.md before relying on them.

The one rule for manual mocks

Every protocol requirement gets two members in the mock class:

  1. Runtime member — fulfills the protocol, forwards to the spy: adapt(super.method, args)
  2. Interaction member — same name, ArgMatcher<T> parameters, returns Interaction<Inputs..., Effect, Output> — what when(...)/verify(...) consume
class FooMock: Mock, @unchecked Sendable, Foo {
    func price(_ item: String) throws -> Int {
        return try adaptThrowing(super.price, item)
    }
    func price(_ item: ArgMatcher<String>) -> Interaction<String, Throws, Int> {
        Interaction(item, spy: super.price)
    }
}

Class shell: inherit Mock first, restate @unchecked Sendable, conform to the most-derived protocol only, match access levels.

Full recipe (inheritance flattening, properties, subscripts, variadics, generics, statics, initializers): manual-mocking.md. Zero-parameter methods and property getters need the pinned-spy pattern described there — the macro-generated form silently mis-stubs them.

Quick verification checklist

A correct mock (manual or generated) round-trips:

let mock = FooMock()
when(mock.price(.any)).thenReturn(42)
let svc: Foo = mock                               // protocol-typed: avoids overload traps
_ = try svc.price("apple")
verify(mock.price(.equal("apple"))).called(1)

Known sharp edges

  • @Mockable on protocol B: A → compile error does not conform to protocol 'A' (inherited requirements never generated). Hand-write per manual-mocking.md.
  • Zero-arg members (func start(), property getters): when(mock.getX()).thenReturn(v) does not reach the runtime member in macro-generated mocks. Manual mocks fix this with the pinned-spy pattern.
  • Stub API is thenReturn / thenThrow / do — there is no .then.
  • Spy names come from the requirement, ignoring argument labels: methods use their name, subscripts are namespaced as subscript+ParameterNames (subscript(row:column:)subscriptRowColumn), settable members add set+Name. The prefix keeps a subscript from colliding with a method or variable of the same name. The compiler already rejects most same-key cases (two subscripts differing only by argument label, or a var x beside a func x(), are both invalid redeclarations); the one that compiles but mocks incorrectly is two methods differing only by argument label (fetch(id:)/fetch(name:)), which silently share a spy — rename one or vary the parameter types. Same-name/different-signature overloads are fine.
  • Settable members ({ get set }) record reads and writes on separate spies: verify(mock.x) counts reads, verify(mock.x <- v) counts writes. A write never registers as a read.
  • Bare mock.start() (zero-arg) is ambiguous on the mock type — call via a protocol-typed reference.
  • Literal arguments on the mock type dispatch to the interaction member (mock.fetchUser(id: "1") returns an Interaction instead of calling through) — call the mock via a protocol-typed reference.

References

  • manual-mocking.md — hand-writing mock classes; inheritance chains; pinned-spy pattern
  • usage.md — when/verify/matchers/stubbing reference
  • sendable.md — Swift 6 concurrency contract and non-Sendable workarounds

関連スキル

steipete/notion

Notion CLI/API for pages, Markdown content, data sources, files, comments, search, Workers, and raw API calls.

community

affaan-m/seo

Audit, plan, and implement SEO improvements across technical SEO, on-page optimization, structured data, Core Web Vitals, and content strategy. Use when the user wants better search visibility, SEO remediation, schema markup, sitemap/robots work, or keyword mapping.

community

affaan-m/brand-voice

Build a source-derived writing style profile from real posts, essays, launch notes, docs, or site copy, then reuse that profile across content, outreach, and social workflows. Use when the user wants voice consistency without generic AI writing tropes.

community

affaan-m/crosspost

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

community

affaan-m/x-api

X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically.

community

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

community