/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.
$ npx -y skills add rshankras/claude-code-apple-skills --skill tdd-feature --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/tdd-feature
Context preview
The summary Claude sees to decide when to auto-load this skill.
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.
SKILL.md
tdd-feature.SKILL.mdname: 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
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 {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.
Repo: rshankras/claude-code-apple-skills
Other skills on rshankras-apple-skills.
- /app-store
App Store optimization and marketing skills for descriptions, screenshots, keywords, review responses, and comprehensive promotional strategy. Use when user needs help with App Store presence, ASO, marketing, or customer communication.
Open skill - /ad-attribution
Privacy-preserving ad measurement with AdAttributionKit (SKAdNetwork's successor) — install and re-engagement attribution, conversion-value strategy under crowd anonymity, and end-to-end postback testing. Use when running paid acquisition beyond Apple Ads, measuring
Open skill - /app-description-writer
Generate compelling App Store descriptions that convert browsers into users. Use when writing initial descriptions, improving existing copy, or drafting promotional text and What's New for a major update.
Open skill - /apple-search-ads
Apple Search Ads campaign strategy for indie developers — paid acquisition, keyword bidding, budget planning, and ROAS optimization. Use when user asks about running ads, paid user acquisition, or Apple Search Ads campaigns.
Open skill - /iap-finalizer
Take a one-time in-app purchase from MISSING_METADATA to READY_TO_SUBMIT in App Store Connect — set its price schedule and localized display name/description (and optional review screenshot) via the ASC REST API. Use at Phase 6 (Pre-Release), after the IAP is built in-app (Phase
Open skill - /keyword-optimizer
Optimize app title, subtitle, and keywords for maximum App Store discoverability. Use when launching a new app, improving search rankings, entering new markets/languages, or safely optimizing ASO for an app with existing traffic.
Open skill

