{ "$schema": "https://raw.githubusercontent.com/guitaripod/wwdc-sessions/master/schema/session.schema.json", "schemaVersion": 1, "id": "wwdc2026-267", "event": "wwdc2026", "eventName": "WWDC26", "year": 2026, "eventContentId": 267, "title": "Migrate to Swift Testing", "description": "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.", "url": "https://developer.apple.com/videos/play/wwdc2026/267", "sosumiURL": "https://sosumi.ai/videos/play/wwdc2026/267", "type": "Video", "publishedAt": "2026-06-08T14:00:00-07:00", "platforms": [ "iOS", "iPadOS", "macOS", "tvOS", "visionOS", "watchOS" ], "keywords": [], "topics": [ "Developer Tools", "Swift" ], "primaryTopic": "Swift", "duration": 1285, "hasTranscript": true, "transcriptWordCount": 2891, "availableLanguages": [ "eng", "fra", "jpn", "kor", "por", "spa", "zho" ], "media": { "hls": "https://devstreaming-cdn.apple.com/videos/wwdc/2026/267/4/d54e4861-10d9-4d4d-9952-3fe311cd2dc4/cmaf.m3u8", "downloadHLS": "https://devstreaming-cdn.apple.com/videos/wwdc/2026/267/4/d54e4861-10d9-4d4d-9952-3fe311cd2dc4/cmaf-download.m3u8", "downloadSD": "https://devstreaming-cdn.apple.com/videos/wwdc/2026/267/4/d54e4861-10d9-4d4d-9952-3fe311cd2dc4/downloads/wwdc2026-267_sd.mp4", "downloadHD": "https://devstreaming-cdn.apple.com/videos/wwdc/2026/267/4/d54e4861-10d9-4d4d-9952-3fe311cd2dc4/downloads/wwdc2026-267_hd.mp4", "duration": 1285 }, "codeSnippets": [ { "title": "Name a test using a raw identifier", "language": "swift", "startTimeSeconds": 72, "code": "import Testing\n\n@testable import DemoApp\n\n@Test func `Default climate: tropical`() async throws {\n let fruit = Fruit(name: \"Coconut\")\n #expect(fruit.climate == .tropical)\n}" }, { "title": "Wrap XCTFail in a test helper function", "language": "swift", "startTimeSeconds": 303, "code": "func testUniqueFruitNames() async throws {\n assertUnique(Market.fruits + [Fruit.lychee])\n}\n\n// TestHelpers.swift\n\nfunc assertUnique(_ fruits: [Fruit], file: StaticString = #filePath, line: UInt = #line) {\n var uniqueNames = Set()\n for name in fruits.map(\\.name) {\n if !uniqueNames.insert(name).inserted {\n XCTFail(\"Duplicate name: \\(name)\", file: file, line: line)\n }\n }\n}" }, { "title": "Replace XCTFail with Issue.record in the test helper", "language": "swift", "startTimeSeconds": 612, "code": "import Testing\n\nfunc assertUnique(_ fruits: [Fruit], sourceLocation: SourceLocation = ...) {\n var uniqueNames = Set()\n for name in fruits.map(\\.name) {\n if !uniqueNames.insert(name).inserted {\n Issue.record(\"Duplicate name: \\(name)\", sourceLocation: sourceLocation)\n }\n }\n}" }, { "title": "Run Swift Package tests with the strict interoperability mode from Terminal", "language": "bash", "startTimeSeconds": 735, "code": "> SWIFT_TESTING_XCTEST_INTEROP_MODE=strict swift test" }, { "title": "Common migration: skipping tests", "language": "swift", "startTimeSeconds": 790, "code": "let isFall = false\n\n// XCTest\nfunc testSwallowFallMigration() async throws {\n try XCTSkipIf(!isFall, \"Wrong season for migration\")\n // ...\n}\n\n// Test.cancel interoperability from Swift Testing\nfunc testSwallowFallMigration() async throws {\n if !isFall {\n try Test.cancel(\"Wrong season for migration\")\n }\n // ...\n}\n\n// ✅ Prefer test trait in Swift Testing\n@Test(.enabled(if: isFall, \"Wrong season for migration\"))\nfunc `Swallow fall migration`() async throws {\n // ...\n}" }, { "title": "Common migration: halting after test failures", "language": "swift", "startTimeSeconds": 821, "code": "func testExample() async throws {\n #expect(Fruit.banana.climate == .temperate)\n\n try #require(Fruit.banana == Fruit.plantain)\n XCTFail(\"This is never reached\")\n}" }, { "title": "Example of nested loops which can be converted into a parameterized @Test function", "language": "swift", "startTimeSeconds": 957, "code": "struct BirdTests {\n\n @Test func `Birds flap wings successfully`() async throws {\n for bird in Aviary.birds {\n for count in (40...100) {\n try await bird.flapWings(count: count)\n }\n }\n }\n\n}" }, { "title": "Refactor nested loops into a parameterized @Test function", "language": "swift", "startTimeSeconds": 1007, "code": "struct BirdTests {\n\n @Test(arguments: Aviary.birds, 40...100)\n func `Birds flap wings successfully`(bird: Bird, count: Int) async throws {\n try await bird.flapWings(count: count)\n }\n\n}" }, { "title": "Precondition check on empty input name in an initializer", "language": "swift", "startTimeSeconds": 1101, "code": "// In `Bird.init(...)`\nif name.isEmpty {\n preconditionFailure(\"Bird name cannot be empty\")\n}" }, { "title": "Add coverage for precondition failure with exit test", "language": "swift", "startTimeSeconds": 1167, "code": "extension BirdTests {\n\n @Test func `Bird with empty name crashes`() async throws {\n await #expect(processExitsWith: .failure) {\n _ = Bird(name: \"\")\n }\n }\n\n}" } ], "resources": [], "path": "sessions/wwdc2026/267-migrate-to-swift-testing/", "files": { "metadata": "sessions/wwdc2026/267-migrate-to-swift-testing/metadata.json", "readme": "sessions/wwdc2026/267-migrate-to-swift-testing/README.md", "transcript": "sessions/wwdc2026/267-migrate-to-swift-testing/transcript.md", "transcriptJSON": "sessions/wwdc2026/267-migrate-to-swift-testing/transcript.json" } }