Expectations
Expectations
Section titled “Expectations”When to use this reference
Section titled “When to use this reference”Use this file when writing assertions, migrating from XCTAssert*, testing thrown errors, or documenting known failures.
#expect as the default
Section titled “#expect as the default”- Use
#expectfor most assertions. - Pass natural Swift expressions (
==,>,.contains,.isEmpty, etc.). - Rely on captured sub-expression values for rich diagnostics in Xcode.
- Avoid old XCTest assertion families in Swift Testing tests.
Example: expressive assertions
Section titled “Example: expressive assertions”import Testing
@Test func pricingRules() { let subtotal = 25 let discount = 5 let total = subtotal - discount
#expect(total == 20) #expect(total > 0) #expect([10, 20, 30].contains(total))}#require for prerequisites
Section titled “#require for prerequisites”- Use
try #require(...)when later assertions depend on this condition. - Treat
#requireas “guard + fail test early”. - Use return value to unwrap optionals safely and reduce noisy optional chaining.
- Prefer this pattern over manual optional checks when failure should halt test flow.
Example: optional precondition + unwrapped usage
Section titled “Example: optional precondition + unwrapped usage”import Testing
@Test func parsedURLHasHTTPS() throws { let value = "https://www.avanderlee.com" let url = try #require(URL(string: value), "URL should parse") #expect(url.scheme == "https")}Throwing behavior checks
Section titled “Throwing behavior checks”- For success-path calls to throwing functions, call directly and assert returned value.
- For expected failure, use throw-aware expectations to verify:
- any throw
- specific error type
- specific error case/value
- Avoid verbose hand-written
do/catchunless custom branching is truly needed.
Example: expected throw and no-throw
Section titled “Example: expected throw and no-throw”import Testing
enum BrewError: Error, Equatable { case missingBeans}
func brew(_ hasBeans: Bool) throws -> String { guard hasBeans else { throw BrewError.missingBeans } return "coffee"}
@Test func expectedThrows() { #expect(throws: BrewError.self) { try brew(false) }}
@Test func expectedNoThrow() { #expect(throws: Never.self) { try brew(true) }}Known issue handling
Section titled “Known issue handling”- Use
withKnownIssuefor temporary expected failures you still want to compile/run. - Prefer
withKnownIssueover blanket disabling when you need ongoing visibility. - Remove known-issue wrappers once failure condition is fixed.
Example: scope only failing section
Section titled “Example: scope only failing section”import Testing
@Test func checkoutFlow() { #expect(true) // still validated
withKnownIssue("Checkout backend intermittently returns 503", isIntermittent: true) { Issue.record("Known upstream issue") }
#expect(2 + 2 == 4) // rest of test still executes}Readability upgrade
Section titled “Readability upgrade”- Conform complex domain types to
CustomTestStringConvertiblefor concise test output. - Keep production
CustomStringConvertibleseparate from test-specific descriptions when needed.
Example: clean diagnostic descriptions
Section titled “Example: clean diagnostic descriptions”import Testing
struct Receipt: CustomTestStringConvertible { let id: UUID let total: Decimal
var testDescription: String { "Receipt(total: \(total))" }}XCTest mapping quick examples
Section titled “XCTest mapping quick examples”// XCTAssertEqual(total, 20)#expect(total == 20)
// try XCTUnwrap(user)let user = try #require(user)
// XCTFail("Unreachable")Issue.record("Unreachable")Do / Don’t
Section titled “Do / Don’t”- Do use
#requirewhen later checks depend on a value. - Do keep
withKnownIssuescopes narrow. - Don’t use XCTest assertions in Swift Testing tests.
- Don’t hide prerequisite failures inside later optional chaining.