Install
$ agentstack add skill-jeremieb-swift-unit-test-instructions-swift-testing ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Swift Testing
Instructions
Step 1: Determine Testing Mode
Check CLAUDE.md for Testing Mode:
enterprise→ consultreferences/enterprise.mdfor strict rulesindie→ consultreferences/indie.mdfor pragmatic rules- If not set: ask the user which mode applies
Step 2: Identify What to Test
Ask if not already clear:
- Which type / function / feature needs tests?
- What is the expected behavior (not implementation)?
- Are there async operations?
- Are there existing mocks or does the skill need to create them?
Always test:
- ViewModels (state changes, actions, error states)
- Services and Repositories (business logic, data transformations)
- Formatters, validators, utilities
- Async flows end-to-end (with mocked dependencies)
- Edge cases and error paths
Never test:
- Private implementation details
- SwiftUI view tree structure or layout positions
- UIViewController internal wiring
Step 3: Choose the Framework
| Scenario | Framework | |---|---| | New unit tests (Swift 5.9+, iOS 16+) | Swift Testing (@Test, #expect) | | Legacy or team convention on XCTest | XCTest (XCTestCase, XCTAssert*) | | UI / end-to-end tests | XCTest + XCUITest | | Async unit tests | Both support async — use await |
Do NOT mix Swift Testing and XCTest in the same test target.
Step 4: Write Tests
Naming (both frameworks):
test_when__should_()
Structure — Swift Testing:
import Testing
@testable import MyApp
@MainActor
struct LoginViewModelTests {
@Test func test_when_credentialsAreValid_should_setIsLoggedIn() async throws {
// Arrange
let authService = MockAuthService(willSucceed: true)
let sut = LoginViewModel(authService: authService)
// Act
await sut.login(email: "user@example.com", password: "secret")
// Assert
#expect(sut.isLoggedIn == true)
#expect(sut.error == nil)
}
@Test func test_when_credentialsAreInvalid_should_setError() async throws {
// Arrange
let authService = MockAuthService(willSucceed: false)
let sut = LoginViewModel(authService: authService)
// Act
await sut.login(email: "bad@example.com", password: "wrong")
// Assert
#expect(sut.isLoggedIn == false)
#expect(sut.error != nil)
}
}
Structure — XCTest:
import XCTest
@testable import MyApp
@MainActor
final class LoginViewModelTests: XCTestCase {
func test_when_credentialsAreValid_should_setIsLoggedIn() async throws {
// Arrange
let authService = MockAuthService(willSucceed: true)
let sut = LoginViewModel(authService: authService)
// Act
await sut.login(email: "user@example.com", password: "secret")
// Assert
XCTAssertTrue(sut.isLoggedIn)
XCTAssertNil(sut.error)
}
}
Step 5: Generate Mocks
Always generate a mock alongside any test that needs one:
// Always inject via protocol, never use real implementations in unit tests
final class MockAuthService: AuthServiceProtocol {
let willSucceed: Bool
private(set) var loginCallCount = 0
init(willSucceed: Bool) {
self.willSucceed = willSucceed
}
func login(email: String, password: String) async throws -> User {
loginCallCount += 1
if willSucceed {
return User(id: "1", email: email)
} else {
throw AuthError.invalidCredentials
}
}
}
Step 6: Determinism Checklist
Before finalizing, verify every test:
- [ ] No real network calls
- [ ] No
sleep()or polling - [ ] No shared global state between tests
- [ ] No real time/date dependency (inject
Dateor a clock protocol) - [ ] Fresh
sutinstance per test - [ ]
setUp/tearDownonly when truly necessary
Examples
Example 1: ViewModel with async fetch
User says: "Write tests for my ProductListViewModel that fetches products from an API"
Actions:
- Read
ProductListViewModel.swiftto understand state and dependencies - Identify the
ProductRepositoryProtocolor create it if missing - Generate
MockProductRepositorywith stubbable results - Write tests: empty state, loaded state, error state, loading indicator
Example 2: Data transformation
User says: "Test my PriceFormatter that converts cents to currency string"
Actions:
- Read
PriceFormatter.swift - Write Swift Testing
@Testfunctions for: zero cents, typical value, large value, locale edge cases
Troubleshooting
Async tests fail intermittently: You are using sleep() or polling. Replace with async/await and mocked async dependencies that resolve immediately.
Tests break on refactoring: You are testing implementation details (private properties, internal call counts). Refocus on observable output state only.
ViewModel is untestable: Dependencies are created inside the type (NetworkService()). Inject them via init. This is an architecture issue — see swift-architecture-audit skill.
"Cannot find type X in scope": Add @testable import YourModule at the top of the test file.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jeremieb
- Source: jeremieb/swift-unit-test-instructions
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.