Section 1/121 menit

1. Masalah yang Dipecahkan

1. Masalah yang Dipecahkan

XCTest: Warisan Objective-C yang Mulai Menua

XCTest didesain pada era Objective-C dan membawa banyak pattern dari sana ke Swift, menciptakan beberapa friction:

1. Harus pakai class dan mewarisi XCTestCase

swift
// XCTest: boilerplate inheritance dan class requirement
class UserServiceTests: XCTestCase {
    var sut: UserService!  // harus var + implicitly unwrapped Optional
    
    override func setUp() { super.setUp()  // harus panggil super
        sut = UserService()
    }
    
    override func tearDown() {
        sut = nil
        super.tearDown()  // harus panggil super
    }
    
    func testLogin() {  // harus prefix 'test' — konvensi runtime, bukan compile-time
        // ...
    }
}

2. Assertion messages yang buruk

swift
// XCTest: pesan error tidak informatif
XCTAssertEqual(user.name, "Ari Supriatna")
// Gagal: XCTAssertEqual failed: ("Ari") is not equal to ("Ari Supriatna")
// Tidak ada konteks: di mana? Nilai yang ada apa?

// Swift Testing: pesan yang kaya informasi
#expect(user.name == "Ari Supriatna")
// Gagal: Expectation failed: (user.name → "Ari") == "Ari Supriatna"
// Jelas: kita tahu nilai aktual dan yang diharapkan

3. Tidak ada parameterized test built-in

swift
// XCTest: harus tulis manual atau pakai library eksternal
func testValidateEmail_valid() { XCTAssertTrue(validate("a@b.com")) }
func testValidateEmail_noAt() { XCTAssertFalse(validate("ab.com")) }
func testValidateEmail_noDomain() { XCTAssertFalse(validate("a@")) }
// Banyak fungsi, banyak duplikasi

// Swift Testing: satu fungsi dengan banyak input
@Test("Email validation", arguments: ["a@b.com", "x@y.z", "user@domain.org"])
func validateValidEmails(email: String) {
    #expect(isValidEmail(email))
}

4. Async testing yang verbose

swift
// XCTest: expectation + wait + timeout
func testAsyncFetch() {
    let expectation = self.expectation(description: "fetch completes")
    service.fetch { result in
        XCTAssertNotNil(result)
        expectation.fulfill()
    }
    wait(for: [expectation], timeout: 5.0)
}

// Swift Testing: native async/await
@Test
func fetchUser() async throws {
    let user = try await service.fetch(id: "123")
    #expect(user.id == "123")
}