Testing

45.1 Swift Testing (Swift 6 / Xcode 16+)

Features: parameterized tests, traits (.enabled(if:), .serialized, .tags, .timeLimit), parallel execution by default, confirmation() for event expectations, an init/deinit setup/teardown, exit tests (6.2), and attachments (6.2).

import Testing
@testable import WeatherKit

@Suite("Temperature conversion")
struct TemperatureTests {
    @Test func freezingPoint() {
        #expect(Temperature(celsius: 0).fahrenheit == 32)
    }

    @Test("Converts known values", arguments: [
        (0.0, 32.0), (100.0, 212.0), (-40.0, -40.0),
    ])
    func conversion(celsius: Double, fahrenheit: Double) {
        #expect(Temperature(celsius: celsius).fahrenheit == fahrenheit)
    }

    @Test func parsingFailsForGarbage() throws {
        #expect(throws: ParseError.self) { try Temperature(parsing: "abc") }
        let value = try #require(Temperature(text: "21.5"))   // unwrap or fail the test
        #expect(value.celsius == 21.5)
    }

    @Test(.disabled("Flaky on CI"), .tags(.network))
    func networkFetch() async throws {
        let forecast = try await WeatherService().forecast(for: "Pune")
        #expect(!forecast.days.isEmpty)
    }

    @Test(.timeLimit(.minutes(1)), .bug("https://github.com/org/repo/issues/42"))
    func longRunning() async {}
}

extension Tag { @Tag static var network: Self }

45.2 XCTest (Classic)

UI testing: XCUITest (XCUIApplication, accessibility identifiers). Snapshot testing: swift-snapshot-testing (Point-Free). Mocking via protocols plus test doubles. Code coverage in Xcode (or swift test --enable-code-coverage).

Testability patterns: dependency injection through protocols, @testable import, avoiding singletons, the Clock protocol for time (TestClock), injecting URLProtocol mocks for networking.

import XCTest
@testable import WeatherKit

final class ParserTests: XCTestCase {
    var parser: Parser!

    override func setUp() { parser = Parser() }
    override func tearDown() { parser = nil }

    func testParsesTemperature() throws {
        let result = try parser.parse("28C")
        XCTAssertEqual(result.celsius, 28, accuracy: 0.001)
    }

    func testThrowsOnEmpty() {
        XCTAssertThrowsError(try parser.parse("")) { error in
            XCTAssertEqual(error as? ParseError, .empty)
        }
    }

    func testAsync() async throws {
        let value = try await parser.parseRemote()
        XCTAssertNotNil(value)
    }

    func testPerformance() {
        measure { _ = (0..<10_000).map { $0 * 2 } }
    }
}