Streaming is available in most browsers,
and in the Developer app.
-
Meet Swift Testing
Introducing Swift Testing: a new package for testing your code using Swift. Explore the building blocks of its powerful new API, discover how it can be applied in common testing workflows, and learn how it relates to XCTest and open source Swift.
Chapters
- 0:00 - Introduction
- 0:59 - Agenda
- 1:20 - Building blocks
- 1:58 - Building blocks: @Test functions
- 3:07 - Building blocks: Expectations (#expect and #require)
- 6:02 - Building blocks: Traits
- 6:49 - Building blocks: @Suite types
- 8:34 - Building blocks: Designed for Swift
- 9:14 - Common workflows
- 9:29 - Common workflows: Tests with conditions
- 10:56 - Common workflows: Tests with common characteristics
- 13:13 - Common workflows: Tests with different arguments
- 17:35 - Swift Testing and XCTest
- 21:52 - Open source
- 23:29 - Wrap up
Resources
- Adding tests to your Xcode project
- Forum: Developer Tools & Services
- Improving code assessment by organizing tests into test plans
- Running tests and interpreting results
- Swift Testing
- Swift Testing GitHub repository
- Swift Testing vision document
Related Videos
WWDC24
-
DownloadArray
-
-
1:54 - Write your first @Test function
import Testing @Test func videoMetadata() { // ... }
-
2:35 - Validate an expected condition using #expect
import Testing @testable import DestinationVideo @Test func videoMetadata() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) }
-
4:24 - Fix a bug in the code being tested
// In `Video.init(...)` self.metadata = Metadata(forContentsOfUrl: url)
-
6:06 - Add a display name to a @Test function
@Test("Check video metadata") func videoMetadata() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) }
-
6:58 - Add a second @Test function
@Test func rating() async throws { let video = Video(fileName: "By the Lake.mov") #expect(video.contentRating == "G") }
-
7:18 - Organize @Test functions into a suite type
struct VideoTests { @Test("Check video metadata") func videoMetadata() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) } @Test func rating() async throws { let video = Video(fileName: "By the Lake.mov") #expect(video.contentRating == "G") } }
-
8:04 - Factor a common value into a stored property in the suite
struct VideoTests { let video = Video(fileName: "By the Lake.mov") @Test("Check video metadata") func videoMetadata() { let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) } @Test func rating() async throws { #expect(video.contentRating == "G") } }
-
9:32 - Specify a runtime condition trait for a @Test function
@Test(.enabled(if: AppFeatures.isCommentingEnabled)) func videoCommenting() { // ... }
-
9:49 - Unconditionally disable a @Test function
@Test(.disabled("Due to a known crash")) func example() { // ... }
-
10:15 - Include a bug trait with a URL along with other traits
@Test(.disabled("Due to a known crash"), .bug("example.org/bugs/1234", "Program crashes at <symbol>")) func example() { // ... }
-
10:33 - Conditionalize a test based on OS version
@Test @available(macOS 15, *) func usesNewAPIs() { // ... }
-
10:42 - Prefer @available on @Test function instead of #available within the function
// ❌ Avoid checking availability at runtime using #available @Test func hasRuntimeVersionCheck() { guard #available(macOS 15, *) else { return } // ... } // ✅ Prefer @available attribute on test function @Test @available(macOS 15, *) func usesNewAPIs() { // ... }
-
11:22 - Add a tag to a @Test function
@Test(.tags(.formatting)) func rating() async throws { #expect(video.contentRating == "G") }
-
11:48 - Add another data formatting-related test with the same tag
@Test(.tags(.formatting)) func formattedDuration() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(video.formattedDuration == "0m 19s") }
-
11:56 - Group related tests into a sub-suite
struct MetadataPresentation { let video = Video(fileName: "By the Lake.mov") @Test(.tags(.formatting)) func rating() async throws { #expect(video.contentRating == "G") } @Test(.tags(.formatting)) func formattedDuration() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(video.formattedDuration == "0m 19s") } }
-
12:05 - Move common tags trait to @Suite attribute, so the suite's @Test functions will inherit the tag
@Suite(.tags(.formatting)) struct MetadataPresentation { let video = Video(fileName: "By the Lake.mov") @Test func rating() async throws { #expect(video.contentRating == "G") } @Test func formattedDuration() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(video.formattedDuration == "0m 19s") } }
-
13:27 - Example of some repetitive tests which can be consolidated into a parameterized @Test function
struct VideoContinentsTests { @Test func mentionsFor_A_Beach() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "A Beach")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } @Test func mentionsFor_By_the_Lake() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } @Test func mentionsFor_Camping_in_the_Woods() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "Camping in the Woods")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } // ...and more, similar test functions }
-
14:07 - Refactor several similar tests into a parameterized @Test function
struct VideoContinentsTests { @Test("Number of mentioned continents", arguments: [ "A Beach", "By the Lake", "Camping in the Woods", "The Rolling Hills", "Ocean Breeze", "Patagonia Lake", "Scotland Coast", "China Paddy Field", ]) func mentionedContinentCounts(videoName: String) async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: videoName)) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } }
-
// Using a for…in loop to repeat a test (not recommended) @Test func mentionedContinentCounts() async throws { let videoNames = [ "A Beach", "By the Lake", "Camping in the Woods", ] let videoLibrary = try await VideoLibrary() for videoName in videoNames { let video = try #require(await videoLibrary.video(named: videoName)) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } }
-
17:15 - Refactor a test using a for…in loop into a parameterized @Test function
@Test(arguments: [ "A Beach", "By the Lake", "Camping in the Woods", ]) func mentionedContinentCounts(videoName: String) async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: videoName)) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) }
-
22:47 - A newly-created Swift package with two simple @Test functions
import Testing @testable import MyLibrary @Test func example() throws { #expect("abc" == "abc") } @Test func failingExample() throws { #expect(123 == 456) }
-
22:56 - Running all tests for the package from Terminal
> swift test
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.