Section 9/161 menit

9. Parameterized Tests Lanjutan

9. Parameterized Tests Lanjutan

CustomTestStringConvertible

Saat test case gagal, Swift Testing menampilkan parameter dalam pesan error. Untuk tipe kustom, implementasikan CustomTestStringConvertible:

swift
struct PriceTestCase {
    let amount: Decimal
    let currency: String
    let expectedFormatted: String
}

// Membuat pesan error lebih readable
extension PriceTestCase: CustomTestStringConvertible {
    var testDescription: String {
        "\(amount) \(currency)\"\(expectedFormatted)\""
    }
}

@Test("Format harga", arguments: [
    PriceTestCase(amount: 10000, currency: "IDR", expectedFormatted: "Rp10.000"),
    PriceTestCase(amount: 1500000, currency: "IDR", expectedFormatted: "Rp1.500.000"),
    PriceTestCase(amount: 9.99, currency: "USD", expectedFormatted: "$9.99"),
    PriceTestCase(amount: 0, currency: "IDR", expectedFormatted: "Rp0"),
])
func testPriceFormatting(_ testCase: PriceTestCase) {
    let result = PriceFormatter.format(testCase.amount, currency: testCase.currency)
    #expect(result == testCase.expectedFormatted)
    // Jika gagal, pesan: "10000 IDR → "Rp10.000""
}

Zip Arguments — Kombinasi Parameter

swift
// Jalankan test dengan kombinasi parameter yang dipasangkan
@Test("Konversi unit", arguments: zip(
    [1.0, 5.0, 100.0],       // input
    [1000.0, 5000.0, 100000.0]  // expected output (meter ke milimeter)
))
func testUnitConversion(meters: Double, expectedMillimeters: Double) {
    #expect(UnitConverter.metersToMillimeters(meters) == expectedMillimeters)
}

Cartesian Product — Semua Kombinasi

swift
// Semua kombinasi input × output (bukan pasangan)
@Test("Operasi aritmatika", arguments: [1, 2, 5], [10, 20, 50])
func testArithmeticOperations(multiplier: Int, base: Int) {
    let result = Calculator.multiply(multiplier, base)
    #expect(result == multiplier * base)
    // Akan dijalankan 9 kali: (1,10), (1,20), (1,50), (2,10), ... (5,50)
}

Parameterized dari External Data

swift
// Load test cases dari JSON file
struct APITestCase: Codable, CustomTestStringConvertible {
    let endpoint: String
    let method: String
    let expectedStatus: Int
    var testDescription: String { "\(method) \(endpoint)\(expectedStatus)" }
}

func loadAPITestCases() throws -> [APITestCase] {
    let url = Bundle.module.url(forResource: "api_test_cases", withExtension: "json")!
    let data = try Data(contentsOf: url)
    return try JSONDecoder().decode([APITestCase].self, from: data)
}

// Parameterized dari file — pastikan load-nya tidak throw
@Test("API endpoint tests", arguments: try loadAPITestCases())
func testAPIEndpoints(_ testCase: APITestCase) async throws {
    let response = try await APIClient.request(
        endpoint: testCase.endpoint,
        method: testCase.method
    )
    #expect(response.statusCode == testCase.expectedStatus)
}