Section 4/161 menit
4. Custom Traits
4. Custom Traits
Traits memungkinkan Anda menambahkan metadata, mengubah perilaku test, atau skip test berdasarkan kondisi kustom.
Membuat TestTrait Kustom
swift
import Testing
// Trait yang menandai test sebagai "slow" — butuh lebih dari 1 detik
struct SlowTestTrait: TestTrait, SuiteTrait {
let estimatedDuration: Duration
var isRecursive: Bool { true } // Berlaku ke nested suites juga
func prepare(for test: Test) async throws {
// Bisa melakukan setup khusus sebelum slow test
// Misalnya: pastikan tidak jalan di CI yang cepat jika tidak diperlukan
if ProcessInfo.processInfo.environment["CI_FAST_ONLY"] != nil {
throw XCTSkip("Slow test dilewati di fast CI environment")
}
}
}
extension Trait where Self == SlowTestTrait {
static func slow(estimated duration: Duration = .seconds(5)) -> SlowTestTrait {
SlowTestTrait(estimatedDuration: duration)
}
}
// Penggunaan
@Test(.slow(estimated: .seconds(10)))
func testDatabaseMigrationPerformance() async throws {
// Test yang memang butuh waktu lama
let result = try await performFullMigration()
#expect(result.success)
}
Trait untuk Environment Condition
swift
// Trait yang skip test jika environment tertentu tidak ada
struct RequiresEnvironmentTrait: TestTrait {
let variableName: String
let description: String
func prepare(for test: Test) async throws {
guard ProcessInfo.processInfo.environment[variableName] != nil else {
throw XCTSkip("\(description) tidak tersedia di environment ini")
}
}
}
extension Trait where Self == RequiresEnvironmentTrait {
static func requiresEnvironment(_ variable: String, description: String) -> RequiresEnvironmentTrait {
RequiresEnvironmentTrait(variableName: variable, description: description)
}
}
// Test yang butuh API key di environment
@Test(.requiresEnvironment("OPENAI_API_KEY", description: "OpenAI API Key"))
func testExternalAPIIntegration() async throws {
let client = ExternalAPIClient()
let result = try await client.fetchData()
#expect(!result.isEmpty)
}
Trait untuk Setup dan Teardown Kustom
swift
// Trait yang otomatis cleanup file temporary setelah test
struct TemporaryDirectoryTrait: TestTrait {
var tempDirectoryURL: URL?
mutating func prepare(for test: Test) async throws {
let tempDir = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
tempDirectoryURL = tempDir
}
// Teardown — dipanggil setelah test selesai
func tearDown(for test: Test) async throws {
if let url = tempDirectoryURL {
try? FileManager.default.removeItem(at: url)
}
}
}
// Trait gabungan untuk test yang butuh network + retry
struct NetworkIntegrationTrait: TestTrait {
let maxRetries: Int
func prepare(for test: Test) async throws {
var lastError: Error?
for attempt in 1...maxRetries {
do {
try await checkNetworkConnectivity()
return // Berhasil
} catch {
lastError = error
if attempt < maxRetries {
try await Task.sleep(nanoseconds: UInt64(attempt) * 1_000_000_000)
}
}
}
throw lastError ?? NetworkError.unavailable
}
}