Skip to content
Development
Skill

/test-contract

Generate protocol/interface test suites that any implementation must pass. Define the contract once, test every implementation. Use when designing protocols or swapping implementations.

From plugin
rshankras-apple-skills
848181 skills

Claude Code

$ npx -y skills add rshankras/claude-code-apple-skills --skill test-contract --agent claude-code

SKILL.md

test-contract.SKILL.md
name: test-contract
description: Generate protocol/interface test suites that any implementation must pass. Define the contract once, test every implementation. Use when designing protocols or swapping implementations.
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion]
last_verified: 2026-07-16
review_by: 2027-06-22

Test Contract

Define behavioral contracts as test suites. Any class conforming to a protocol must pass the contract tests. Ensures consistent behavior across implementations — human-written, AI-generated, or mock.

When This Skill Activates

Use this skill when the user:

  • Designs a protocol and wants to ensure conformance quality
  • Has multiple implementations of the same interface (e.g., real + mock + in-memory)
  • Says "test the protocol" or "contract test"
  • Wants to swap implementations safely (e.g., CoreData → SwiftData)
  • Wants to ensure AI-generated implementations meet the spec

Why Contract Tests

Protocol: DataStore
  ├── SQLiteDataStore  → must pass contract tests
  ├── InMemoryDataStore → must pass contract tests
  ├── MockDataStore    → must pass contract tests
  └── CloudDataStore   → must pass contract tests

Write the contract once. Every implementation proves it works by passing the same tests.

Process

Phase 1: Identify the Protocol

Grep: "protocol.*\\{" to find protocols
Read: protocol definition

Understand:

  • [ ] All required methods and properties
  • [ ] Expected behavior for each method
  • [ ] Error conditions and edge cases
  • [ ] Thread safety requirements
  • [ ] State invariants (e.g., count matches items.count)

Phase 2: Define Contract Behaviors

For each protocol method, define required behaviors:

## DataStore Contract

### save(_ item:)
- Saving a new item increases count by 1
- Saved item is retrievable by ID

### fetch(id:)
- Returns item when it exists
- Returns nil when item doesn't exist
- Returns most recently saved version

### delete(id:)
- Deleting existing item decreases count by 1
- Deleted item is no longer fetchable

### fetchAll()
- Returns all saved items
- Returns empty array when store is empty
- Items are in consistent order (insertion or specified)

### Invariants
- count == fetchAll().count (always)
- save then fetch returns same item
- delete then fetch returns nil

Phase 3: Generate Contract Test Suite

Pattern: Generic Test Suite

import Testing

/// Contract tests for any DataStore implementation.
/// Subclass or call with your concrete implementation.
struct DataStoreContractTests<Store: DataStore> {

    let makeStore: () -> Store

    init(makeStore: @escaping () -> Store) {
        self.makeStore = makeStore
    }

    // MARK: - save(_ item:)

    @Test("save increases count")
    func saveIncreasesCount() async throws {
        let store = makeStore()
        let item = Item(id: "1", title: "Test")

        try await store.save(item)

        #expect(store.count == 1)
    }

    @Test("save then fetch returns same item")
    func saveAndFetch() async throws {
        let store = makeStore()
        let item = Item(id: "1", title: "Test")

        try await store.save(item)
        let fetched = try await store.fetch(id: "1")

        #expect(fetched?.title == "Test")
    }

    @Test("save existing item updates it")
    func saveUpdates() async throws {
        let store = makeStore()
        try await store.save(Item(id: "1", title: "Original"))
        try await store.save(Item(id: "1", title: "Updated"))

        let fetched = try await store.fetch(id: "1")

        #expect(fetched?.title == "Updated")
        #expect(store.count == 1)
    }

    @Test("save throws on invalid item")
    func saveInvalidThrows() async {
        let store = makeStore()
        let invalid = Item(id: "1", title: "")

        await #expect(throws: DataStoreError.invalidItem) {
            try await store.save(invalid)
        }
    }

    // MARK: - fetch(id:)

    @Test("fetch returns nil for non-existent ID")
    func fetchNonExistent() async throws {
        let store = makeStore()

        let result = try await store.fetch(id: "nonexistent")

        #expect(result == nil)
    }

    // MARK: - delete(id:)

    @Test("delete removes item")
    func deleteRemoves() async throws {
        let store = makeStore()
        try await store.save(Item(id: "1", title: "Test"))

        try await store.delete(id: "1")

        #expect(store.count == 0)
        let fetched = try await store.fetch(id: "1")
        #expect(fetched == nil)
    }

    @Test("delete non-existent does nothing")
    func deleteNonExistent() async throws {
        let store = makeStore()

        try await store.delete(id: "nonexistent")

        #expect(store.count == 0)
    }

    // MARK: - fetchAll()

    @Test("fetchAll returns all items")
    func fetchAll() async throws {
        let store = makeStore()
        try await store.save(Item(id: "1", title: "A"))
        try await store.save(Item(id: "2", title: "B"))

        let all = try await store.fetchAll()

        #expect(all.count == 2)
    }

    @Test("fetchAll returns empty when store is empty")
    func fetchAllEmpty() async throws {
        let store = makeStore()

        let all = try await store.fetchAll()

        #expect(all.isEmpty)
    }

    // MARK: - Invariants

    @Test("count matches fetchAll count")
    func countInvariant() async throws {
        let store = makeStore()
        try await store.save(Item(id: "1", title: "A"))
        try await store.save(Item(id: "2", title: "B"))
        try await store.delete(id: "1")

        let all = try await store.fetchAll()

        #expect(store.count == all.count)
    }
}

Running Contract Tests Per Implementation

@Suite("InMemoryDataStore Contract")
struct InMemoryDataStoreContractTests {
    let contract = DataStoreContractTests { InMemoryDataStore() }

    @Test("save increases count")
    func saveIncreasesCount() async thro
Read more
@rshankras
Ships withrshankras-apple-skills

A collection of Claude Code skills for iOS, macOS, watchOS, visionOS, and Apple platform development. These skills help you plan and build apps, maintain code quality, ensure HIG compliance, and guide you from idea to App Store.

Stars on GitHub

Momentum

stars in total
848
Star history of rshankras/claude-code-apple-skills on GitHub, a chart by star-history.com

Other skills on rshankras-apple-skills.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about test-contract.

Which coding agents can use test-contract?
test-contract installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.