.agents/skills/swift-testing-expert/references/performance-and-best-practices.md
Use this file when test runs are slow, flaky, or not scaling in CI, and when you need practical patterns for fast, deterministic Swift Testing suites.
.serialized as a temporary compromise, not a default architecture.Synchronous tests generally run faster and are easier to reason about.
import Testing
struct PriceCalculator {
static func total(_ subtotal: Int, discount: Int) -> Int { subtotal - discount }
}
@Test func totalCalculation() {
#expect(PriceCalculator.total(100, discount: 20) == 80)
}
Avoid introducing async or sleeps for purely synchronous logic.
// Avoid:
// @Test func totalCalculation() async {
// try await Task.sleep(nanoseconds: 100_000_000)
// #expect(...)
// }
@MainActor can reduce useful parallelization and should be used only when code truly needs main-thread isolation.
import Testing
// Good: non-UI logic stays non-main-actor.
@Test func parserIsStable() {
#expect("A,B,C".split(separator: ",").count == 3)
}
// Use @MainActor only for UI/main-thread sensitive code.
@MainActor
@Test func viewModelMutation() {
#expect(true)
}
Shared mutable state is a major source of flakiness and parallel failures.
import Testing
enum Globals {
static var token: String?
}
// Flaky pattern:
@Test func writeToken() {
Globals.token = "abc"
#expect(Globals.token == "abc")
}
@Test func expectsNoToken() {
#expect(Globals.token == nil)
}
Better: create isolated state per test.
import Testing
struct SessionState {
var token: String?
}
@Test func isolatedTokenState() {
var state = SessionState()
state.token = "abc"
#expect(state.token == "abc")
}
Use fakes/in-memory repositories for high-volume test runs; reserve real integration dependencies for dedicated plans.
import Testing
protocol CacheStore {
func put(key: String, value: String)
func get(key: String) -> String?
}
final class InMemoryCacheStore: CacheStore {
private var values: [String: String] = [:]
func put(key: String, value: String) { values[key] = value }
func get(key: String) -> String? { values[key] }
}
@Test func cacheRoundTrip() {
let cache = InMemoryCacheStore()
cache.put(key: "user", value: "42")
#expect(cache.get(key: "user") == "42")
}
import Testing
func isValidPort(_ value: Int) -> Bool { (1...65535).contains(value) }
@Test(arguments: [1, 80, 443, 65535])
func validPorts(_ port: Int) {
#expect(isValidPort(port))
}
This reduces duplicated setup code and gives argument-level failure visibility.
import Testing
struct CurrencyTests {
let rates: [String: Double]
init() {
rates = ["USD": 1.0, "EUR": 0.92]
}
@Test func hasEURRate() {
#expect(rates["EUR"] != nil)
}
}
.serialized narrowlyimport Testing
@Suite(.serialized)
struct TemporarySerialDBTests {
@Test func migrationA() async throws { #expect(true) }
@Test func migrationB() async throws { #expect(true) }
}
Add TODO context and remove once dependencies are isolated.
@MainActor or .serialized to silence flakiness.