-
Migrate to Swift Testing
Learn how to fearlessly adopt Swift Testing alongside your XCTests using test framework interoperability. Discover best practices and patterns for incrementally introducing advanced testing features that accelerate development and increase coverage.
Chapters
- 0:07 - Introduction
- 1:08 - Swift Testing basics
- 2:50 - Migration strategy
- 5:48 - Test framework interoperability
- 7:43 - Interoperability modes
- 13:02 - Common migration patterns
- 15:34 - Parameterized tests
- 18:02 - Exit tests
- 20:04 - Next steps
Resources
Related Videos
WWDC26
WWDC24
-
Search this video…
-
-
1:12 - Name a test using a raw identifier
import Testing @testable import DemoApp @Test func `Default climate: tropical`() async throws { let fruit = Fruit(name: "Coconut") #expect(fruit.climate == .tropical) } -
5:03 - Wrap XCTFail in a test helper function
func testUniqueFruitNames() async throws { assertUnique(Market.fruits + [Fruit.lychee]) } // TestHelpers.swift func assertUnique(_ fruits: [Fruit], file: StaticString = #filePath, line: UInt = #line) { var uniqueNames = Set<String>() for name in fruits.map(\.name) { if !uniqueNames.insert(name).inserted { XCTFail("Duplicate name: \(name)", file: file, line: line) } } } -
10:12 - Replace XCTFail with Issue.record in the test helper
import Testing func assertUnique(_ fruits: [Fruit], sourceLocation: SourceLocation = ...) { var uniqueNames = Set<String>() for name in fruits.map(\.name) { if !uniqueNames.insert(name).inserted { Issue.record("Duplicate name: \(name)", sourceLocation: sourceLocation) } } } -
12:15 - Run Swift Package tests with the strict interoperability mode from Terminal
> SWIFT_TESTING_XCTEST_INTEROP_MODE=strict swift test -
13:10 - Common migration: skipping tests
let isFall = false // XCTest func testSwallowFallMigration() async throws { try XCTSkipIf(!isFall, "Wrong season for migration") // ... } // Test.cancel interoperability from Swift Testing func testSwallowFallMigration() async throws { if !isFall { try Test.cancel("Wrong season for migration") } // ... } // ✅ Prefer test trait in Swift Testing @Test(.enabled(if: isFall, "Wrong season for migration")) func `Swallow fall migration`() async throws { // ... } -
13:41 - Common migration: halting after test failures
func testExample() async throws { #expect(Fruit.banana.climate == .temperate) try #require(Fruit.banana == Fruit.plantain) XCTFail("This is never reached") } -
15:57 - Example of nested loops which can be converted into a parameterized @Test function
struct BirdTests { @Test func `Birds flap wings successfully`() async throws { for bird in Aviary.birds { for count in (40...100) { try await bird.flapWings(count: count) } } } } -
16:47 - Refactor nested loops into a parameterized @Test function
struct BirdTests { @Test(arguments: Aviary.birds, 40...100) func `Birds flap wings successfully`(bird: Bird, count: Int) async throws { try await bird.flapWings(count: count) } } -
18:21 - Precondition check on empty input name in an initializer
// In `Bird.init(...)` if name.isEmpty { preconditionFailure("Bird name cannot be empty") } -
19:27 - Add coverage for precondition failure with exit test
extension BirdTests { @Test func `Bird with empty name crashes`() async throws { await #expect(processExitsWith: .failure) { _ = Bird(name: "") } } }
-
-
- 0:07 - Introduction
How to fearlessly migrate from XCTest to Swift Testing using the new interoperability feature.
- 1:08 - Swift Testing basics
A quick review of core Swift Testing building blocks — the @Test macro, #expect, and how they compare to XCTest assertions.
- 2:50 - Migration strategy
Covers the recommended incremental approach: leave existing XCTests in place, and start writing new tests in Swift Testing right away.
- 5:48 - Test framework interoperability
Introduces the interoperability feature that lets you safely call XCTest or Swift Testing API from within a test belonging to the other framework.
- 7:43 - Interoperability modes
Walks through the four interoperability modes — Limited, Complete, Strict, and None — and how to configure them in Xcode Test Plans and Swift packages.
- 13:02 - Common migration patterns
Covers practical patterns you will encounter during migration, including replacing XCTSkip with Test.cancel or traits, and continueAfterFailure with #require.
- 15:34 - Parameterized tests
Shows how to replace loop-based XCTest cases with Swift Testing parameterized tests for faster parallel execution and clearer failure reporting.
- 18:02 - Exit tests
Demonstrates how to use Swift Testing exit tests to cover code paths that call preconditionFailure or crash, running them safely in a child process.
- 20:04 - Next steps
Recaps the migration path, highlights Swift Testing open-source availability and cross-platform support, and encourages community participation.