From apple-dev
Fix bugs using red-green-refactor — reproduce the bug as a failing test first, then fix it. Use when fixing bugs to ensure they never regress.
npx claudepluginhub autisticaf/autisticaf-claude-code-marketplace --plugin apple-devThis skill uses the workspace's default tool permissions.
> **First step:** Tell the user: "testing-tdd-bug-fix skill loaded."
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
First step: Tell the user: "testing-tdd-bug-fix skill loaded."
Fix bugs the right way: reproduce first, fix second, verify always. Especially critical when using AI to generate fixes — the test ensures the AI actually solved the problem.
Use this skill when the user:
Without TDD: With TDD:
Bug reported Bug reported
→ AI generates fix → Write failing test (RED)
→ Deploy → AI generates fix (GREEN)
→ Hope it works → Test passes — confirmed fixed
→ Bug returns later → Test prevents regression forever
Gather information:
Grep: "[relevant keyword]" to find the source
Read: the suspected file(s)
Identify:
Write a test that fails because of the bug. This proves the bug exists.
import Testing
@testable import YourApp
@Suite("Bug Fix: [Brief description]")
struct BugFix_DescriptionTests {
@Test("should [expected behavior] — was [actual behavior]")
func reproduceBug() {
// Arrange — set up the conditions that trigger the bug
let calculator = PriceCalculator()
// Act — perform the operation that's broken
let result = calculator.applyDiscount(price: 100, percent: 50)
// Assert — what it SHOULD return (this will FAIL now)
#expect(result == 50.0) // Currently returns 0.5 (bug: divides by 100 twice)
}
}
@Test("should return cached items when network fails — was crashing")
func reproduceBug() async throws {
let mockNetwork = MockNetworkClient(shouldFail: true)
let mockCache = MockCache(items: [.sample])
let service = DataService(network: mockNetwork, cache: mockCache)
// This should return cached data, but currently crashes
let items = try await service.fetchItems()
#expect(items.count == 1)
}
@Test("should update count after deletion — was showing stale count")
func reproduceBug() {
let manager = ItemManager()
manager.addItem(Item(title: "Test"))
manager.deleteItem(at: 0)
#expect(manager.count == 0) // Currently still returns 1
#expect(manager.items.isEmpty) // Currently still contains item
}
@Test("should handle empty input — was crashing with index out of range")
func reproduceBug() {
let parser = CSVParser()
// This crashes with empty string
let result = parser.parse("")
#expect(result.rows.isEmpty)
#expect(result.columns.isEmpty)
}
@Test("should show error state when API fails — was showing infinite spinner")
func reproduceBug() async {
let failingAPI = MockAPI(shouldFail: true)
let viewModel = ListViewModel(api: failingAPI)
await viewModel.loadData()
#expect(viewModel.state == .error) // Currently stuck on .loading
#expect(viewModel.isLoading == false)
}
Run the test — it MUST fail. If it passes, you haven't reproduced the bug.
xcodebuild test -scheme YourApp \
-only-testing "YourAppTests/BugFix_DescriptionTests"
Now fix the code to make the test pass. The fix should be minimal — only change what's needed.
Prompt to Claude: "Here's a failing test that reproduces a bug.
Fix the source code to make this test pass without breaking
any existing tests."
Run the test — it MUST pass now.
# Run the bug fix test
xcodebuild test -scheme YourApp \
-only-testing "YourAppTests/BugFix_DescriptionTests"
# Run ALL tests to check for regressions
xcodebuild test -scheme YourApp
If the fix introduced duplication or the code could be cleaner:
Checklist before marking the bug as fixed:
If a bug has multiple symptoms, write multiple tests:
@Suite("Bug Fix: Discount calculation errors")
struct BugFix_DiscountCalculationTests {
@Test("50% discount on $100 should be $50")
func fiftyPercentDiscount() {
let calc = PriceCalculator()
#expect(calc.applyDiscount(price: 100, percent: 50) == 50.0)
}
@Test("0% discount should return original price")
func zeroDiscount() {
let calc = PriceCalculator()
#expect(calc.applyDiscount(price: 100, percent: 0) == 100.0)
}
@Test("100% discount should return 0")
func fullDiscount() {
let calc = PriceCalculator()
#expect(calc.applyDiscount(price: 100, percent: 100) == 0.0)
}
@Test("discount on $0 should return $0")
func zeroPrice() {
let calc = PriceCalculator()
#expect(calc.applyDiscount(price: 0, percent: 50) == 0.0)
}
}
## Bug Fix: [Description]
### Bug Summary
- **Expected**: [What should happen]
- **Actual**: [What was happening]
- **Root cause**: [Why it was broken]
### Test (RED)
```swift
// Failing test that reproduces the bug
File: path/to/file.swift:XX
// Before (buggy)
// After (fixed)
Test added: Tests/BugFixes/BugFix_DescriptionTests.swift
This test will catch any future regression of this bug.
## Common Pitfalls
| Pitfall | Problem | Solution |
|---------|---------|----------|
| Test passes before fix | You didn't reproduce the bug | Make assertions more specific |
| Fix breaks other tests | Fix was too broad | Revert and use smaller, targeted change |
| Test is too specific | Brittle, breaks on unrelated changes | Test behavior, not implementation details |
| Skipping the red step | No proof the test catches the bug | Always verify test fails first |
| Fixing multiple bugs at once | Can't isolate regressions | One bug = one test + one fix |
## References
- `testing-tdd-feature/` — for new features (not bug fixes)
- `testing-characterization-test-generator/` — for capturing existing behavior first
- `generators-test-generator/` — for general test generation