# Testing with Swift Testing and XCTest — Swift

Source: https://www.skillbyai.com/en/swift/p-testing

> Write unit tests with the Swift Testing framework and XCTest.

## Tests for Swift code

Swift has two testing frameworks. **Swift Testing**, introduced in 2024 with Xcode 16 and Swift 6, uses modern language features: `import Testing`, functions marked with **`@Test`**, expectations with the **`#expect(...)`** macro (which shows the values of sub-expressions when it fails) and **`#require(...)`** to stop a test when a precondition fails (for example unwrapping an optional). Tests can be **parameterised** with `arguments:`, grouped in **suites** (types marked `@Suite`), tagged, and run in parallel by default. **XCTest** is the long-standing framework (`XCTestCase` subclasses, `XCTAssertEqual` and friends) and is still required for **UI tests** and performance tests; the two can coexist in one project. Both support `async` tests. Use `@testable import` to test internal code. Make code testable with **protocols** for dependencies (a `PaymentGateway` protocol with a fake in tests), value-type models and pure functions. Run tests with Xcode's test navigator, `swift test`, or `xcodebuild test` in CI.

## Swift Testing with parameterised cases and an async test

#expect shows values on failure; #require stops early.

```swift
import Testing
@testable import Pricing

@Suite("Shipping fees")
struct ShippingFeeTests {

    @Test("Fee depends on total and speed", arguments: [
        (totalPaise: 99_999, express: false, expected: 4_900),
        (totalPaise: 99_999, express: true,  expected: 9_900),
        (totalPaise: 100_000, express: true, expected: 0),
    ])
    func fee(totalPaise: Int, express: Bool, expected: Int) {
        #expect(ShippingCalculator().feePaise(total: totalPaise, express: express) == expected)
    }

    @Test func parsesAmount() throws {
        let money = try #require(Money(rupees: "499.00"))    // fails the test if nil
        #expect(money.paise == 49_900)
    }

    @Test func checkoutChargesOnce() async throws {
        let gateway = FakePaymentGateway()
        let checkout = Checkout(gateway: gateway)
        _ = try await checkout.pay(orderId: "o-1", amountPaise: 49_900)
        #expect(gateway.charges.count == 1)
    }
}
```

## An inspector who shows the measurements

A plain assertion says "failed". `#expect` is an inspector who writes down every measurement: "fee was 9900, expected 4900, total was 99999", so you know immediately what went wrong.

**Quiz:** In the Swift Testing framework, which macro checks a condition and reports sub-expression values on failure?

- [ ] XCTAssertTrue
- [x] #expect
- [ ] @Test
- [ ] @testable

*Answer:* #expect. #expect is Swift Testing's primary expectation macro.
