Your First Test with @Test

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Defining and Validating Behavior

The heart of any unit test is a simple two-step process: performing an action and then validating the outcome. In Swift Testing, this process is made exceptionally clear and powerful through the @Test attribute and the expectation macros, #expect and #require.

Your First Test with @Test

In the world of XCTest, a function was identified as a test if its name began with the prefix “test”. Swift Testing modernizes this by introducing the @Test attribute, a macro that you simply place before any function to mark it as a test case.  

// In your main app target
struct TemperatureConverter {
  func celsiusToFahrenheit(_ celsius: Double) -> Double {
    return (celsius * 9 / 5) + 32
  }
}

// In your test target
import Testing
@testable import YourAppName // Allows access to internal types

@Test
func testFreezingPointConversion() {
  let converter = TemperatureConverter()
  let fahrenheit = converter.celsiusToFahrenheit(0.0)
  // We will add a validation in the next step
}
@Test("Verify that 0°C converts to 32°F")
func freezingPoint() {
  let converter = TemperatureConverter()
  let fahrenheit = converter.celsiusToFahrenheit(0.0)
  #expect(fahrenheit == 32.0)
}

Making Assertions with #expect

Once you have performed an action in your test, you need to check the result. This is done using an “expectation.” The primary tool for this in Swift Testing is the #expect macro.  

@Test("Verify boiling and body temperature conversions")
func temperatureConversions() {
  let converter = TemperatureConverter()
  
  // Check boiling point
  let boilingFahrenheit = converter.celsiusToFahrenheit(100.0)
  #expect(boilingFahrenheit == 212.0)
  
  // Check approximate human body temperature
  let bodyTempFahrenheit = converter.celsiusToFahrenheit(37.0)
  #expect(bodyTempFahrenheit == 98.6)
}
✘ Expectation failed: (bodyTempFahrenheit → 98.5) == 98.6
enum PasswordError: Error {
  case tooShort
}

func validate(password: String) throws {
  if password.count < 8 {
    throw PasswordError.tooShort
  }
}

@Test("Password validation should throw error for short passwords")
func passwordThrowsError() {
  #expect(throws: PasswordError.tooShort) {
    try validate(password: "12345")
  }
}

Enforcing Preconditions with #require

Sometimes, a test has certain preconditions that must be met for the rest of the test to be meaningful. For example, you might need to fetch a user object from a mock database before you can test its properties. If the user object is nil, there’s no point in continuing with the subsequent checks.

struct Video {
  let title: String
  let duration: Int // in seconds
}

struct VideoLibrary {
  private let videos = [
    "Intro": Video(title: "Intro", duration: 60),
    "Conclusion": Video(title: "Conclusion", duration: 90)
  ]
  
  func video(withTitle title: String) -> Video? {
    return videos[title]
  }
}

@Test("Video duration should be 60 seconds")
func videoDurationTest() throws {
  let library = VideoLibrary()
  
  // Use #require to fetch and unwrap the video.
  // If the video is nil, the test stops here.
  let introVideo = try #require(library.video(withTitle: "Intro"))
  
  // Because 'introVideo' is now a non-optional 'Video',
  // we can directly access its properties.
  #expect(introVideo.duration == 60)
}
XCTest Assertion Swift Testing Equivalent Notes
XCTAssertTrue(condition) #expect(condition) The #expect macro is the universal replacement for boolean checks.
XCTAssertEqual(a, b) #expect(a == b) Use standard Swift operators. Failure messages are richer.
XCTAssertNotEqual(a, b) #expect(a!= b) Leverage the full power of Swift’s expression language.
XCTAssertNil(value) #expect(value == nil) Again, use standard Swift operators for clarity.
XCTUnwrap(optional) let value = try #require(optional) #require is safer, stops execution on failure, and returns the unwrapped value.
XCTAssertThrowsError(...) #expect(throws: SomeError.self) {... } Swift Testing provides a clear, trailing-closure syntax for error checking.
See forum comments
Download course materials from Github
Previous: Swift Testing Next: Grouping Tests with @Suite