All posts

Testing Error Scenarios in Swift Applications

Write effective Swift error scenario tests with XCTest, async error handling, mock protocols, and Result type testing patterns.

Testing Error Scenarios in Swift Applications

Swift's strong type system catches many errors at compile time, but runtime error paths still need thorough testing.

Test Throwing Functions

func testGetUser_invalidId_throwsNotFound() throws {
    let service = UserService(repository: MockUserRepository())

    XCTAssertThrowsError(try service.getUser(id: "invalid")) { error in
        guard let appError = error as? AppError else {
            XCTFail("Expected AppError, got \(error)")
            return
        }
        XCTAssertEqual(appError, .notFound("User not found"))
    }
}

Test Async Error Handling

func testFetchData_networkError_returnsFailure() async {
    let client = MockHTTPClient(error: URLError(.notConnectedToInternet))
    let service = DataService(client: client)

    do {
        _ = try await service.fetchData()
        XCTFail("Expected error")
    } catch let error as AppError {
        XCTAssertEqual(error, .networkUnavailable)
    } catch {
        XCTFail("Unexpected error type: \(error)")
    }
}

Mock Protocols for Failure Injection

protocol DatabaseProtocol {
    func query<T: Decodable>(_ sql: String) async throws -> [T]
}

class MockDatabase: DatabaseProtocol {
    var shouldFail = false
    var error: Error = AppError.databaseUnavailable

    func query<T: Decodable>(_ sql: String) async throws -> [T] {
        if shouldFail { throw error }
        return []
    }
}

func testListOrders_databaseFailure_throwsError() async {
    let db = MockDatabase()
    db.shouldFail = true
    db.error = AppError.databaseUnavailable

    let service = OrderService(database: db)

    do {
        _ = try await service.listOrders()
        XCTFail("Expected error")
    } catch {
        XCTAssertEqual(error as? AppError, .databaseUnavailable)
    }
}

Test Result Types

func testParseConfig_invalidJSON_returnsFailure() {
    let result = ConfigParser.parse("not json")

    switch result {
    case .success:
        XCTFail("Expected failure")
    case .failure(let error):
        XCTAssertTrue(error.localizedDescription.contains("invalid"))
    }
}

Error Scenarios to Test

  • Network failures — timeout, no connection, server errors
  • Decoding errors — malformed JSON, missing fields
  • Permission errors — unauthorized access
  • Concurrency issues — actor isolation violations
  • Resource exhaustion — memory warnings, disk full

Use Bugsly to track errors from real devices in production. Each crash or non-fatal error should inspire a corresponding test case to prevent regression.

Try Bugsly Free

AI-powered error tracking that explains your bugs. Set up in 2 minutes, free forever for small projects.

Get Started Free