Skip to content
Development
Skill

/tdd-feature

Red-green-refactor scaffold for building new features with TDD. Write failing tests first, then implement to pass. Use when building new features test-first.

From plugin
rshankras-apple-skills
848181 skills

Claude Code

$ npx -y skills add rshankras/claude-code-apple-skills --skill tdd-feature --agent claude-code

SKILL.md

tdd-feature.SKILL.md
name: tdd-feature
description: Red-green-refactor scaffold for building new features with TDD. Write failing tests first, then implement to pass. Use when building new features test-first.
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion]
last_verified: 2026-07-16
review_by: 2027-06-22

TDD Feature

Build new features using the red-green-refactor cycle. Tests define the spec, AI generates the implementation, tests verify correctness.

When This Skill Activates

Use this skill when the user:

  • Wants to "TDD a new feature" or "build test-first"
  • Says "I want tests before code"
  • Asks for "red-green-refactor" workflow
  • Wants AI to generate code that's provably correct
  • Is building a new module, service, or feature from scratch

Why TDD for New Features with AI

Traditional:     AI generates code → You hope it's correct → Ship → Find bugs
TDD with AI:     You write tests (spec) → AI generates code to pass → Proven correct

The test is your **acceptance criteria in code form**. AI excels at going from failing test to passing implementation — it's a concrete, unambiguous target.

Process

Phase 1: Define the Feature

Before writing any code or tests, understand:

1. **What does this feature do?** (user story or requirement) 2. **What are the inputs?** (parameters, user actions, data) 3. **What are the outputs?** (return values, state changes, UI updates) 4. **What are the edge cases?** (empty, nil, error, boundary) 5. **What dependencies does it need?** (network, storage, other services)

Phase 2: Design the API Surface

Sketch the public interface before writing tests:

// Example: Designing a FavoriteManager
protocol FavoriteManaging {
    func add(_ item: Item) async throws
    func remove(_ item: Item) async throws
    func isFavorite(_ item: Item) -> Bool
    var favorites: [Item] { get }
    var count: Int { get }
}

This doesn't need to compile yet — it's the contract you'll test against.

Phase 3: RED — Write Failing Tests

Write tests for each behavior. Start with the simplest case and build up.

Order of Tests (Simple → Complex)

1. **Construction** — can you create the object? 2. **Happy path** — does the basic operation work? 3. **State verification** — does state update correctly? 4. **Edge cases** — empty, nil, boundaries 5. **Error handling** — what fails and how? 6. **Integration** — does it work with dependencies?

Template: Feature Test Suite

import Testing
@testable import YourApp

@Suite("FavoriteManager")
struct FavoriteManagerTests {

    // 1. Construction
    @Test("starts with empty favorites")
    func startsEmpty() {
        let manager = FavoriteManager()
        #expect(manager.favorites.isEmpty)
        #expect(manager.count == 0)
    }

    // 2. Happy path
    @Test("can add a favorite")
    func addFavorite() async throws {
        let manager = FavoriteManager()
        let item = Item(id: "1", title: "Test")

        try await manager.add(item)

        #expect(manager.count == 1)
        #expect(manager.isFavorite(item))
    }

    // 3. State verification
    @Test("can remove a favorite")
    func removeFavorite() async throws {
        let manager = FavoriteManager()
        let item = Item(id: "1", title: "Test")
        try await manager.add(item)

        try await manager.remove(item)

        #expect(manager.count == 0)
        #expect(!manager.isFavorite(item))
    }

    // 4. Edge cases
    @Test("adding duplicate does not increase count")
    func addDuplicate() async throws {
        let manager = FavoriteManager()
        let item = Item(id: "1", title: "Test")

        try await manager.add(item)
        try await manager.add(item)

        #expect(manager.count == 1)
    }

    @Test("removing non-existent item does nothing")
    func removeNonExistent() async throws {
        let manager = FavoriteManager()
        let item = Item(id: "1", title: "Test")

        try await manager.remove(item)

        #expect(manager.count == 0)
    }

    // 5. Error handling
    @Test("throws when storage is full")
    func storageFullError() async {
        let manager = FavoriteManager(maxCapacity: 2)
        let items = (1...3).map { Item(id: "\($0)", title: "Item \($0)") }

        await #expect(throws: FavoriteError.capacityExceeded) {
            for item in items {
                try await manager.add(item)
            }
        }
    }

    // 6. Ordering
    @Test("favorites are in insertion order")
    func insertionOrder() async throws {
        let manager = FavoriteManager()
        let items = ["C", "A", "B"].map { Item(id: $0, title: $0) }

        for item in items {
            try await manager.add(item)
        }

        #expect(manager.favorites.map(\.title) == ["C", "A", "B"])
    }
}

**Run tests — they should ALL fail** (the type doesn't even exist yet).

Phase 4: GREEN — Implement to Pass

Now implement the feature. Pass the **tests as context to AI**:

Prompt to Claude: "Here are my failing tests for FavoriteManager.
Implement the FavoriteManager class to make all tests pass.
Follow the protocol FavoriteManaging."

Implementation Rules

  • **One test at a time** — make the first test pass, then the second, etc.
  • **Write the simplest code** that passes each test
  • **Don't anticipate future tests** — only satisfy current failing tests
  • **Run tests after each change**
xcodebuild test -scheme YourApp \
  -only-testing "YourAppTests/FavoriteManagerTests"

Phase 5: REFACTOR

With all tests green, clean up the implementation:

  • Extract helper methods
  • Improve naming
  • Remove duplication
  • Optimize performance (if tests cover perf requirements)

**Run tests after every refactor step.** If any test fails, you've changed behavior — revert.

Phase 6: Integration

Once the unit is solid, write integration tests:

@Suite("FavoriteManager Integration")
struct FavoriteManagerIntegrationTests {
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 tdd-feature.

Which coding agents can use tdd-feature?
tdd-feature 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.