skills/generators-test-generator/SKILL.md
Generate test file boilerplate for unit tests, integration tests, and UI tests. Use when user asks to "add tests" or "write tests" for existing code. For TDD workflows, use testing-tdd-feature or testing-tdd-bug-fix. For Swift Testing API reference, use swift-testing.
npx skillsauth add AutisticAF/claude-code-apple-dev-plugin generators-test-generatorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
First step: Tell the user: "generators-test-generator skill loaded."
Generate test templates for unit tests, integration tests, and UI tests in iOS/macOS apps.
Use this skill when the user:
Do NOT activate for these — use the specialized skill instead:
testing-tdd-feature or testing-tdd-bug-fix@Test, #expect, traits, parameterized tests) → swift-testingtesting-snapshot-test-setuptesting-tdd-refactor-guard or testing-characterization-test-generatortesting-test-contracttesting-test-data-factorytesting-integration-test-scaffoldWhat tests do you need?
|
+-- Unit tests for business logic
| +-- Swift Testing (@Test, #expect) -- recommended for iOS 16+
| +-- XCTest -- for iOS 13-15 support or existing XCTest projects
|
+-- Integration tests (component interactions)
| +-- Protocol-based mocks with dependency injection
|
+-- UI tests
| +-- XCUITest with Screen Object pattern
|
+-- Snapshot/preview tests
+-- PreviewSnapshots or swift-snapshot-testing
Search for existing test infrastructure:
Glob: **/*Tests.swift, **/*Tests/**/*.swift, **/*Spec.swift
Grep: "import XCTest" or "import Testing" or "@Suite" or "@Test"
Grep: "MockItemRepository" or "protocol.*Repository" or "class Mock"
If existing tests are found:
If a test target already exists:
Grep: "ViewModel" or "Reducer" or "UseCase" or "Repository" or "Service"
Glob: **/*ViewModel.swift, **/*Reducer.swift, **/*Repository.swift
This determines which test templates to generate (ViewModel tests, Reducer tests, etc.).
Tests/UnitTests/
├── ViewModelTests/
│ └── ItemViewModelTests.swift
├── ServiceTests/
│ └── APIClientTests.swift
└── RepositoryTests/
└── ItemRepositoryTests.swift
Tests/UITests/
├── Screens/
│ └── HomeScreenTests.swift
├── Flows/
│ └── OnboardingFlowTests.swift
└── Helpers/
└── TestHelpers.swift
import Testing
@testable import YourApp
@Suite("Item ViewModel Tests")
struct ItemViewModelTests {
@Test("loads items successfully")
func loadsItems() async throws {
let mockRepository = MockItemRepository()
let viewModel = ItemViewModel(repository: mockRepository)
await viewModel.loadItems()
#expect(viewModel.items.count == 3)
#expect(viewModel.isLoading == false)
}
@Test("handles empty state")
func handlesEmptyState() async {
let mockRepository = MockItemRepository(items: [])
let viewModel = ItemViewModel(repository: mockRepository)
await viewModel.loadItems()
#expect(viewModel.items.isEmpty)
#expect(viewModel.showEmptyState)
}
}
@Test("validates email format", arguments: [
("[email protected]", true),
("invalid", false),
("no@tld", false),
("[email protected]", true)
])
func validatesEmail(email: String, isValid: Bool) {
#expect(EmailValidator.isValid(email) == isValid)
}
import XCTest
@testable import YourApp
final class ItemViewModelTests: XCTestCase {
var sut: ItemViewModel!
var mockRepository: MockItemRepository!
override func setUp() {
super.setUp()
mockRepository = MockItemRepository()
sut = ItemViewModel(repository: mockRepository)
}
override func tearDown() {
sut = nil
mockRepository = nil
super.tearDown()
}
func testLoadsItems() async throws {
await sut.loadItems()
XCTAssertEqual(sut.items.count, 3)
XCTAssertFalse(sut.isLoading)
}
}
@Suite("ViewModel Tests")
struct ViewModelTests {
@Test("state transitions correctly")
func stateTransitions() async {
let vm = ItemViewModel(repository: MockItemRepository())
#expect(vm.state == .idle)
await vm.loadItems()
#expect(vm.state == .loaded)
}
@Test("error handling")
func errorHandling() async {
let failingRepo = MockItemRepository(shouldFail: true)
let vm = ItemViewModel(repository: failingRepo)
await vm.loadItems()
#expect(vm.state == .error)
#expect(vm.errorMessage != nil)
}
}
@Test("fetches data asynchronously")
func fetchesData() async throws {
let service = APIService()
let result = try await service.fetchItems()
#expect(result.count > 0)
}
@Test("times out appropriately")
func timesOut() async {
await #expect(throws: TimeoutError.self) {
try await withTimeout(seconds: 1) {
try await Task.sleep(for: .seconds(5))
}
}
}
protocol ItemRepository {
func fetchItems() async throws -> [Item]
func saveItem(_ item: Item) async throws
}
final class MockItemRepository: ItemRepository {
var items: [Item] = []
var shouldFail = false
var saveCallCount = 0
func fetchItems() async throws -> [Item] {
if shouldFail {
throw TestError.mockFailure
}
return items
}
func saveItem(_ item: Item) async throws {
saveCallCount += 1
items.append(item)
}
}
import XCTest
final class HomeScreen {
let app: XCUIApplication
init(app: XCUIApplication) {
self.app = app
}
var itemList: XCUIElement {
app.collectionViews["itemList"]
}
var addButton: XCUIElement {
app.buttons["addItem"]
}
func tapItem(at index: Int) {
itemList.cells.element(boundBy: index).tap()
}
func addNewItem(title: String) {
addButton.tap()
app.textFields["itemTitle"].tap()
app.textFields["itemTitle"].typeText(title)
app.buttons["save"].tap()
}
}
In Xcode:
YourAppTests)# Command line
xcodebuild test -scheme YourApp -destination 'platform=iOS Simulator,name=iPhone 16'
# With coverage
xcodebuild test -scheme YourApp -enableCodeCoverage YES
| # | Mistake | Why It's Wrong | Fix |
|---|---------|---------------|-----|
| 1 | Testing implementation details instead of behavior | Tests break on every refactor, providing no safety net | Test public API and observable outcomes, not internal state |
| 2 | Sharing mutable state between tests | Tests pass individually but fail when run together (order-dependent) | Create fresh instances in each test; use init() in @Suite structs or setUp() in XCTest |
| 3 | Using XCTAssertTrue(result != nil) instead of XCTUnwrap | Failure message is useless ("XCTAssertTrue failed") with no context | Use let value = try XCTUnwrap(result) or #expect(result != nil) with Swift Testing |
| 4 | Not testing error paths | Only happy-path coverage; errors crash in production | Always test with shouldFail = true mocks and verify error state |
| 5 | Real network calls in unit tests | Tests are slow, flaky, and fail offline | Use protocol-based mocks; reserve real network calls for integration test schemes |
Before finishing test generation, verify:
loadsItemsSuccessfully not testLoadItems)async throws (Swift Testing) or async throws with expectations (XCTest)#expect(items.count == 3) not #expect(!items.isEmpty)try #require() (Swift Testing) or XCTUnwrap (XCTest)xcodebuild test or Xcode test navigatordevelopment
SwiftUI Layout protocol for custom container layouts including flow layouts, radial layouts, and animated transitions. Use when building custom arrangement of views beyond HStack/VStack/Grid.
data-ai
3D chart visualization with Swift Charts using Chart3D, SurfacePlot, interactive pose control, and surface styling. Use when creating 3D data visualizations.
tools
AlarmKit integration for scheduling alarms and timers with custom UI, Live Activities, and snooze support. Use when implementing alarm or timer features in iOS 18+ apps.
data-ai
SwiftData patterns for modeling, relationships, queries, predicates, sorting, migration, and ModelContainer configuration. Use when working with SwiftData persistence.