Skip to content

Swift Testing Scoping (SwiftUI)

A SwiftUI guide for coding agents. Also covers testscoping protocol, swift testing traits, mock environment tests, tasklocal dependency injection, reusable test setup teardown, suite trait scope.

Swift 6.1 adds test scoping to Swift Testing: a custom trait that runs code around a test or a whole suite. Pair it with a @TaskLocal dependency container and one annotation swaps the app's environment for mocks.

Setup without scoping repeats itself

Swift Testing uses init and deinit on a class suite for setup and teardown. Every suite that needs the same ModelContainer or seed data repeats that initializer.

class ModelTests {
  let container: ModelContainer

  init() throws {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    container = try ModelContainer(for: User.self, configurations: config)
  }
}

A TaskLocal environment to swap

Hold dependencies in one struct and expose the live instance as a @TaskLocal with the production value as the default. Task locals can be overridden for the duration of one closure, which is exactly the shape a test scope needs.

struct Environment {
  let search: (String) async throws -> [String]
}

extension Environment {
  @TaskLocal static var current = Environment.production
}

The scoping trait

Conform to TestTrait, SuiteTrait, and TestScoping. The single requirement, provideScope, receives the test's performing function; you must call it or the test never runs. Wrap the call in the task-local override.

struct MockEnvironmentTrait: TestTrait, SuiteTrait, TestScoping {
  func provideScope(
    for test: Test,
    testCase: Test.Case?,
    performing function: @Sendable () async throws -> Void
  ) async throws {
    try await Environment.$current.withValue(Environment.mock) {
      try await function()
    }
  }
}

extension Trait where Self == MockEnvironmentTrait {
  static var mockedEnvironment: Self { Self() }
}

Apply per test or per suite

@Test(.mockedEnvironment) func verifySomething() async throws {
  Environment.current   // the mocked environment
}

@Suite(.mockedEnvironment) struct ExamplesTests { /* every test mocked */ }

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-introducing-swift-testing-scoping" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems