Unit Test vs Integration Test
Unit tests versus integration tests: scope, speed, reliability, ROI, and how to balance the testing pyramid for production-grade applications.
Apple's new open-source test library — macro-based, built natively for Swift Concurrency
A ~13-year-old, battle-tested framework — the only place for UI and Performance tests
The trend is clear, but a wholesale migration is an unnecessary risk. Write new unit and integration tests in Swift Testing — you get less code, automatic value capture, built-in parameterized tests, and parallelism. Leave UI and Performance Tests in XCTest; that's what Apple's documentation asks for. Since the two frameworks can coexist in the same target and Swift 6.4 added cross-framework reporting, a wholesale migration isn't necessary — move file by file.
| Category | Swift Testing | XCTest |
|---|---|---|
| Performance | 8/10 | 8/10 |
| Ease of Learning | 7/10 | 6/10 |
| Ecosystem | 6/10 | 8/10 |
| Community | 6/10 | 8/10 |
| Job Market | 5/10 | 7/10 |
| Future-Proof | 9/10 | 6/10 |
// Swift Testing — parameterized async unit test
import Testing
@testable import PaymentKit
@Suite("Price calculation")
struct PriceCalculatorTests {
@Test("Discount is calculated correctly for valid coupon codes",
arguments: [
("SAVE10", 100.0, 90.0),
("SAVE20", 100.0, 80.0),
("NONE", 100.0, 100.0)
])
func discountIsApplied(code: String, base: Double, expected: Double) async throws {
let calculator = PriceCalculator()
let result = try await calculator.applyDiscount(code: code, to: base)
#expect(result == expected, "expected \(expected) for \(code), got \(result)")
}
@Test("Invalid coupon throws an error")
func invalidCouponThrows() async throws {
let calculator = PriceCalculator()
await #expect(throws: CouponError.invalid) {
try await calculator.applyDiscount(code: "XXX", to: 100.0)
}
}
@Test("Cart total is required", .disabled("Cart service has not been migrated yet"))
func cartTotalRequired() async throws {
let calculator = PriceCalculator()
try #require(calculator.cartTotal > 0)
}
}// XCTest — same scenario (price calculation) + UI test
import XCTest
@testable import PaymentKit
final class PriceCalculatorTests: XCTestCase {
var calculator: PriceCalculator!
override func setUpWithError() throws {
calculator = PriceCalculator()
}
func testDiscountIsAppliedForKnownCoupons() async throws {
let cases: [(String, Double, Double)] = [
("SAVE10", 100.0, 90.0),
("SAVE20", 100.0, 80.0),
("NONE", 100.0, 100.0)
]
for (code, base, expected) in cases {
let result = try await calculator.applyDiscount(code: code, to: base)
XCTAssertEqual(result, expected, "coupon: \(code)")
}
}
// XCTAssertThrowsError is synchronous; use do/catch + XCTFail in an async call
func testInvalidCouponThrows() async throws {
do {
_ = try await calculator.applyDiscount(code: "XXX", to: 100.0)
XCTFail("expected an error")
} catch {
XCTAssertEqual(error as? CouponError, .invalid)
}
}
}
// UI test — only possible with XCTest/XCUITest
final class CheckoutUITests: XCTestCase {
func testApplyCouponButtonUpdatesTotal() throws {
let app = XCUIApplication()
app.launch()
app.textFields["couponField"].tap()
app.textFields["couponField"].typeText("SAVE10")
app.buttons["applyCouponButton"].tap()
XCTAssertEqual(app.staticTexts["totalLabel"].label, "$90.00")
}
}The trend is clear, but a wholesale migration is an unnecessary risk. Write new unit and integration tests in Swift Testing — you get less code, automatic value capture, built-in parameterized tests, and parallelism. Leave UI and Performance Tests in XCTest; that's what Apple's documentation asks for. Since the two frameworks can coexist in the same target and Swift 6.4 added cross-framework reporting, a wholesale migration isn't necessary — move file by file.
Get Free ConsultationNo. Swift Testing is Apple's recommended default for new unit and integration tests, but XCTest hasn't been removed; XCUITest (UI automation) and XCTMetric (performance testing) still live only in XCTest. The two frameworks can coexist in the same target, and since Swift 6.4 they interoperate through cross-framework issue reporting.