View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Todos los videos
  • Información

Más videos

  • Información
  • Código
  • Go further with Swift Testing

    Learn how to write a sweet set of (test) suites using Swift Testing's baked-in features. Discover how to take the building blocks further and use them to help expand tests to cover more scenarios, organize your tests across different suites, and optimize your tests to run in parallel.

    Capítulos

    • 0:00 - Introduction
    • 0:36 - Why we write tests
    • 0:51 - Challenges in testing
    • 1:21 - Writing expressive code
    • 1:35 - Expectations
    • 3:58 - Required expectations
    • 4:29 - Tests with known issues
    • 5:54 - Custom test descriptions
    • 7:23 - Parameterized testing
    • 12:47 - Organizing tests
    • 12:58 - Test suites
    • 13:33 - The tag trait
    • 20:38 - Xcode Cloud support
    • 21:09 - Testing in parallel
    • 21:36 - Parallel testing basics
    • 24:26 - Asynchronous conditions
    • 26:32 - Wrap up

    Recursos

    • Swift Testing vision document
    • Swift Testing GitHub repository
    • Running tests and interpreting results
    • Adding tests to your Xcode project
    • Swift Testing
    • Forum: Developer Tools & Services
    • Improving code assessment by organizing tests into test plans
      • Video HD
      • Video SD

    Videos relacionados

    WWDC24

    • Meet Swift Testing

    WWDC21

    • Meet async/await in Swift
  • Buscar este video…
    • 0:01 - Successful throwing function

      // Expecting errors
      
      import Testing
      
      @Test func brewTeaSuccessfully() throws {
          let teaLeaves = TeaLeaves(name: "EarlGrey", optimalBrewTime: 4)
          let cupOfTea = try teaLeaves.brew(forMinutes: 3)
      }
    • 0:02 - Validating a successful throwing function

      import Testing
      
      @Test func brewTeaSuccessfully() throws {
          let teaLeaves = TeaLeaves(name: "EarlGrey", optimalBrewTime: 4)
          let cupOfTea = try teaLeaves.brew(forMinutes: 3)
          #expect(cupOfTea.quality == .perfect)
      }
    • 0:03 - Validating an error is thrown with do-catch (not recommended)

      import Testing
      
      @Test func brewTeaError() throws {
          let teaLeaves = TeaLeaves(name: "EarlGrey", optimalBrewTime: 3)
      
          do {
              try teaLeaves.brew(forMinutes: 100)
          } catch is BrewingError {
              // This is the code path we are expecting
          } catch {
              Issue.record("Unexpected Error")
          }
      }
    • 0:04 - Validating a general error is thrown

      import Testing
      
      @Test func brewTeaError() throws {
          let teaLeaves = TeaLeaves(name: "EarlGrey", optimalBrewTime: 4)
          #expect(throws: (any Error).self) {
              try teaLeaves.brew(forMinutes: 200) // We don't want this to fail the test!
          }
      }
    • 0:05 - Validating a type of error

      import Testing
      
      @Test func brewTeaError() throws {
          let teaLeaves = TeaLeaves(name: "EarlGrey", optimalBrewTime: 4)
          #expect(throws: BrewingError.self) {
              try teaLeaves.brew(forMinutes: 200) // We don't want this to fail the test!
          }
      }
    • 0:06 - Validating a specific error

      import Testing
      
      @Test func brewTeaError() throws {
          let teaLeaves = TeaLeaves(name: "EarlGrey", optimalBrewTime: 4)
          #expect(throws: BrewingError.oversteeped) {
              try teaLeaves.brew(forMinutes: 200) // We don't want this to fail the test!
          }
      }
    • 0:07 - Complicated validations

      import Testing
      
      @Test func brewTea() {
          let teaLeaves = TeaLeaves(name: "EarlGrey", optimalBrewTime: 4)
          #expect {
              try teaLeaves.brew(forMinutes: 3)
          } throws: { error in
              guard let error = error as? BrewingError,
                    case let .needsMoreTime(optimalBrewTime) = error else {
                  return false
              }
              return optimalBrewTime == 4
          }
      }
    • 0:08 - Throwing expectation

      import Testing
      
      @Test func brewAllGreenTeas() {
        #expect(throws: BrewingError.self) {
          brewMultipleTeas(teaLeaves: ["Sencha", "EarlGrey", "Jasmine"], time: 2)
        }
      }
    • 0:09 - Required expectations

      import Testing
      
      @Test func brewAllGreenTeas() throws {
        try #require(throws: BrewingError.self) {
          brewMultipleTeas(teaLeaves: ["Sencha", "EarlGrey", "Jasmine"], time: 2)
        }
      }
    • 0:10 - Control flow of validating an optional value (not recommended)

      import Testing
      
      struct TeaLeaves {symbols
          let name: String
          let optimalBrewTime: Int
      
          func brew(forMinutes minutes: Int) throws -> Tea { ... }
      }
      
      @Test func brewTea() throws {
          let teaLeaves = TeaLeaves(name: "Sencha", optimalBrewTime: 2)
          let brewedTea = try teaLeaves.brew(forMinutes: 100)
          guard let color = brewedTea.color else {
              Issue.record("Tea color was not available!")
          }
          #expect(color == .green)
      }
    • 0:11 - Failing test with a throwing function

      import Testing
      
      @Test func softServeIceCreamInCone() throws {
          try softServeMachine.makeSoftServe(in: .cone)
      }
    • 0:12 - Disabling a test with a throwing function (not recommended)

      import Testing
      
      @Test(.disabled) func softServeIceCreamInCone() throws {
          try softServeMachine.makeSoftServe(in: .cone)
      }
    • 0:13 - Wrapping a failing test in withKnownIssue

      import Testing
      
      @Test func softServeIceCreamInCone() throws {
          withKnownIssue {
              try softServeMachine.makeSoftServe(in: .cone)
          }
      }
    • 0:14 - Wrap just the failing section in withKnownIssue

      import Testing
      
      @Test func softServeIceCreamInCone() throws {
          let iceCreamBatter = IceCreamBatter(flavor: .chocolate)
          try #require(iceCreamBatter != nil)
          #expect(iceCreamBatter.flavor == .chocolate)
      
          withKnownIssue {
              try softServeMachine.makeSoftServe(in: .cone)
          }
      }
    • 0:15 - Simple enumerations

      import Testing
      
      enum SoftServe {
          case vanilla, chocolate, pineapple
      }
    • 0:16 - Complex types

      import Testing
      
      struct SoftServe {
          let flavor: Flavor
          let container: Container
          let toppings: [Topping]
      }
      
      @Test(arguments: [
          SoftServe(flavor: .vanilla, container: .cone, toppings: [.sprinkles]),
          SoftServe(flavor: .chocolate, container: .cone, toppings: [.sprinkles]),
          SoftServe(flavor: .pineapple, container: .cup, toppings: [.whippedCream])
      ])
      func softServeFlavors(_ softServe: SoftServe) { /*...*/ }
    • 0:17 - Conforming to CustomTestStringConvertible

      import Testing
      
      struct SoftServe: CustomTestStringConvertible {
          let flavor: Flavor
          let container: Container
          let toppings: [Topping]
      
          var testDescription: String {
              "\(flavor) in a \(container)"
          }
      }
      
      @Test(arguments: [
          SoftServe(flavor: .vanilla, container: .cone, toppings: [.sprinkles]),
          SoftServe(flavor: .chocolate, container: .cone, toppings: [.sprinkles]),
          SoftServe(flavor: .pineapple, container: .cup, toppings: [.whippedCream])
      ])
      func softServeFlavors(_ softServe: SoftServe) { /*...*/ }
    • 0:18 - An enumeration with a computed property

      extension IceCream {
          enum Flavor {
              case vanilla, chocolate, strawberry, mintChip, rockyRoad, pistachio
      
              var containsNuts: Bool {
                  switch self {
                  case .rockyRoad, .pistachio:
                      return true
                  default:
                      return false
                  }
              }
          }
      }
    • 0:19 - A test function for a specific case of an enumeration

      import Testing
      
      @Test func doesVanillaContainNuts() throws {
          try #require(!IceCream.Flavor.vanilla.containsNuts)
      }
    • 0:20 - Separate test functions for all cases of an enumeration

      import Testing
      
      @Test func doesVanillaContainNuts() throws {
          try #require(!IceCream.Flavor.vanilla.containsNuts)
      }
      
      @Test func doesChocolateContainNuts() throws {
          try #require(!IceCream.Flavor.chocolate.containsNuts)
      }
      
      @Test func doesStrawberryContainNuts() throws {
          try #require(!IceCream.Flavor.strawberry.containsNuts)
      }
      
      @Test func doesMintChipContainNuts() throws {
          try #require(!IceCream.Flavor.mintChip.containsNuts)
      }
      
      @Test func doesRockyRoadContainNuts() throws {
          try #require(!IceCream.Flavor.rockyRoad.containsNuts)
      }
    • 0:21 - Parameterizing a test with a for loop (not recommended)

      import Testing
      
      extension IceCream {
          enum Flavor {
              case vanilla, chocolate, strawberry, mintChip, rockyRoad, pistachio
          }
      }
      
      @Test
      func doesNotContainNuts() throws {
          for flavor in [IceCream.Flavor.vanilla, .chocolate, .strawberry, .mintChip] {
              try #require(!flavor.containsNuts)
          }
      }
    • 0:22 - Swift testing parameterized tests

      import Testing
      
      extension IceCream {
          enum Flavor {
              case vanilla, chocolate, strawberry, mintChip, rockyRoad, pistachio
          }
      }
      
      @Test(arguments: [IceCream.Flavor.vanilla, .chocolate, .strawberry, .mintChip])
      func doesNotContainNuts(flavor: IceCream.Flavor) throws {
          try #require(!flavor.containsNuts)
      }
    • 0:23 - 100% test coverage

      import Testing
      
      extension IceCream {
          enum Flavor {
              case vanilla, chocolate, strawberry, mintChip, rockyRoad, pistachio
          }
      }
      
      @Test(arguments: [IceCream.Flavor.vanilla, .chocolate, .strawberry, .mintChip])
      func doesNotContainNuts(flavor: IceCream.Flavor) throws {
          try #require(!flavor.containsNuts)
      }
      
      @Test(arguments: [IceCream.Flavor.rockyRoad, .pistachio])
      func containNuts(flavor: IceCream.Flavor) {
         #expect(flavor.containsNuts)
      }
    • 0:24 - A parameterized test with one argument

      import Testing
      
      enum Ingredient: CaseIterable {
          case rice, potato, lettuce, egg
      }
      
      @Test(arguments: Ingredient.allCases)
      func cook(_ ingredient: Ingredient) async throws {
          #expect(ingredient.isFresh)
          let result = try cook(ingredient)
          try #require(result.isDelicious)
      }
    • 0:26 - Adding a second argument to a parameterized test

      import Testing
      
      enum Ingredient: CaseIterable {
          case rice, potato, lettuce, egg
      }
      
      enum Dish: CaseIterable {
          case onigiri, fries, salad, omelette
      }
      
      @Test(arguments: Ingredient.allCases, Dish.allCases)
      func cook(_ ingredient: Ingredient, into dish: Dish) async throws {
          #expect(ingredient.isFresh)
          let result = try cook(ingredient)
          try #require(result.isDelicious)
          try #require(result == dish)
      }
    • 0:28 - Using zip() on arguments

      import Testing
      
      enum Ingredient: CaseIterable {
          case rice, potato, lettuce, egg
      }
      
      enum Dish: CaseIterable {
          case onigiri, fries, salad, omelette
      }
      
      @Test(arguments: zip(Ingredient.allCases, Dish.allCases))
      func cook(_ ingredient: Ingredient, into dish: Dish) async throws {
          #expect(ingredient.isFresh)
          let result = try cook(ingredient)
          try #require(result.isDelicious)
          try #require(result == dish)
      }
    • 0:29 - Suites

      @Suite("Various desserts") 
      struct DessertTests {
          @Test func applePieCrustLayers() { /* ... */ }
          @Test func lavaCakeBakingTime() { /* ... */ }
          @Test func eggWaffleFlavors() { /* ... */ }
          @Test func cheesecakeBakingStrategy() { /* ... */ }
          @Test func mangoSagoToppings() { /* ... */ }
          @Test func bananaSplitMinimumScoop() { /* ... */ }
      }
    • 0:30 - Nested suites

      import Testing
      
      @Suite("Various desserts")
      struct DessertTests {
          @Suite struct WarmDesserts {
              @Test func applePieCrustLayers() { /* ... */ }
              @Test func lavaCakeBakingTime() { /* ... */ }
              @Test func eggWaffleFlavors() { /* ... */ }
          }
      
          @Suite struct ColdDesserts {
              @Test func cheesecakeBakingStrategy() { /* ... */ }
              @Test func mangoSagoToppings() { /* ... */ }
              @Test func bananaSplitMinimumScoop() { /* ... */ }
          }
      }
    • 0:31 - Separate suites

      @Suite struct DrinkTests {
          @Test func espressoExtractionTime() { /* ... */ }
          @Test func greenTeaBrewTime() { /* ... */ }
          @Test func mochaIngredientProportion() { /* ... */ }
      }
      
      @Suite struct DessertTests {
          @Test func espressoBrownieTexture() { /* ... */ }
          @Test func bungeoppangFilling() { /* ... */ }
          @Test func fruitMochiFlavors() { /* ... */ }
      }
    • 0:32 - Separate suites

      @Suite struct DrinkTests {
          @Test func espressoExtractionTime() { /* ... */ }
          @Test func greenTeaBrewTime() { /* ... */ }
          @Test func mochaIngredientProportion() { /* ... */ }
      }
      
      @Suite struct DessertTests {
          @Test func espressoBrownieTexture() { /* ... */ }
          @Test func bungeoppangFilling() { /* ... */ }
          @Test func fruitMochiFlavors() { /* ... */ }
      }
    • 0:35 - Using a tag

      import Testing 
      
      extension Tag {
          @Tag static var caffeinated: Self
      }
      
      @Suite(.tags(.caffeinated)) struct DrinkTests {
          @Test func espressoExtractionTime() { /* ... */ }
          @Test func greenTeaBrewTime() { /* ... */ }
          @Test func mochaIngredientProportion() { /* ... */ }
      }
      
      @Suite struct DessertTests {
          @Test(.tags(.caffeinated)) func espressoBrownieTexture() { /* ... */ }
          @Test func bungeoppangFilling() { /* ... */ }
          @Test func fruitMochiFlavors() { /* ... */ }
      }
    • 0:36 - Declare and use a second tag

      import Testing 
      
      extension Tag {
          @Tag static var caffeinated: Self
          @Tag static var chocolatey: Self
      }
      
      @Suite(.tags(.caffeinated)) struct DrinkTests {
          @Test func espressoExtractionTime() { /* ... */ }
          @Test func greenTeaBrewTime() { /* ... */ }
          @Test(.tags(.chocolatey)) func mochaIngredientProportion() { /* ... */ }
      }
      
      @Suite struct DessertTests {
          @Test(.tags(.caffeinated, .chocolatey)) func espressoBrownieTexture() { /* ... */ }
          @Test func bungeoppangFilling() { /* ... */ }
          @Test func fruitMochiFlavors() { /* ... */ }
      }
    • 0:37 - Two tests with an unintended data dependency (not recommended)

      import Testing
      
      // ❌ This code is not concurrency-safe.
      
      var cupcake: Cupcake? = nil
      
      @Test func bakeCupcake() async {
          cupcake = await Cupcake.bake(toppedWith: .frosting)
          // ...
      }
      
      @Test func eatCupcake() async {
          await eat(cupcake!)
          // ...
      }
    • 0:38 - Serialized trait

      import Testing
      
      @Suite("Cupcake tests", .serialized)
      struct CupcakeTests {
          var cupcake: Cupcake?
      
          @Test func mixingIngredients() { /* ... */ }
          @Test func baking() { /* ... */ }
          @Test func decorating() { /* ... */ }
          @Test func eating() { /* ... */ }
      }
    • 0:39 - Serialized trait with nested suites

      import Testing
      
      @Suite("Cupcake tests", .serialized)
      struct CupcakeTests {
          var cupcake: Cupcake?
      
          @Suite("Mini birthday cupcake tests")
          struct MiniBirthdayCupcakeTests {
              // ...
          }
      
          @Test(arguments: [...]) func mixing(ingredient: Food) { /* ... */ }
          @Test func baking() { /* ... */ }
          @Test func decorating() { /* ... */ }
          @Test func eating() { /* ... */ }
      }
    • 0:40 - Using async/await in a test

      import Testing
      
      @Test func bakeCookies() async throws {
          let cookies = await Cookie.bake(count: 10)
          try await eat(cookies, with: .milk)
      }
    • 0:41 - Using a function with a completion handler in a test (not recommended)

      import Testing
      
      @Test func bakeCookies() async throws {
          let cookies = await Cookie.bake(count: 10)
          // ❌ This code will run after the test function returns.
          eat(cookies, with: .milk) { result, error in
              #expect(result != nil)
          }
      }
    • 0:42 - Replacing a completion handler with an asynchronous function call

      import Testing
      
      @Test func bakeCookies() async throws {
          let cookies = await Cookie.bake(count: 10)
          try await eat(cookies, with: .milk)
      }
    • 0:43 - Using withCheckedThrowingContinuation

      import Testing
      
      @Test func bakeCookies() async throws {
          let cookies = await Cookie.bake(count: 10)
          try await withCheckedThrowingContinuation { continuation in
              eat(cookies, with: .milk) { result, error in
                  if let result {
                      continuation.resume(returning: result)
                  } else {
                      continuation.resume(throwing: error)
                  }
              }
          }
      }
    • 0:44 - Callback that invokes more than once (not recommended)

      import Testing
      
      @Test func bakeCookies() async throws {
          let cookies = await Cookie.bake(count: 10)
          // ❌ This code is not concurrency-safe.
          var cookiesEaten = 0
          try await eat(cookies, with: .milk) { cookie, crumbs in
              #expect(!crumbs.in(.milk))
              cookiesEaten += 1
          }
          #expect(cookiesEaten == 10)
      }
    • 0:45 - Confirmations on callbacks that invoke more than once

      import Testing
      
      @Test func bakeCookies() async throws {
          let cookies = await Cookie.bake(count: 10)
          try await confirmation("Ate cookies", expectedCount: 10) { ateCookie in
              try await eat(cookies, with: .milk) { cookie, crumbs in
                  #expect(!crumbs.in(.milk))
                  ateCookie()
              }
          }
      }
    • 0:46 - Confirmation that occurs 0 times

      import Testing
      
      @Test func bakeCookies() async throws {
          let cookies = await Cookie.bake(count: 10)
          try await confirmation("Ate cookies", expectedCount: 0) { ateCookie in
              try await eat(cookies, with: .milk) { cookie, crumbs in
                  #expect(!crumbs.in(.milk))
                  ateCookie()
              }
          }
      }

Developer Footer

  • Videos
  • WWDC24
  • Go further with Swift Testing
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • Mini Apps Partner Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines