Tempatkan persiapan fixture asinkron bersama di init() async throws milik suite Swift Testing. Swift Testing menyelesaikan inisialisasi tersebut sebelum memanggil metode pengujian instance, sehingga metode itu menerima state yang siap digunakan. Di XCTest, gunakan setUp() async throws sebagai gantinya.

Panduan ini ditujukan bagi pengembang Mac yang menyiapkan data pengujian unit lokal di Xcode. Tugasnya adalah menyelesaikan inisialisasi asinkron sebelum pernyataan dimulai, dengan menggunakan perhitungan deterministik dan input sintetis. Apple mendokumentasikan pendekatan inisialisasi ini dalam Memigrasikan pengujian dari XCTest.

Bagaimana Swift Testing menunggu inisialisasi fixture?

Untuk metode pengujian instance, Swift Testing menginisialisasi tipe suite yang menampungnya, lalu memanggil metode pada instance tersebut. Suite harus memiliki inisialisasi yang dapat dipanggil tanpa argumen; inisialisasi itu boleh bersifat asinkron, melempar error, atau keduanya. Setiap metode pengujian instance berjalan pada instance yang berbeda. Aturan siklus hidup ini didokumentasikan dalam Mengatur fungsi pengujian dengan tipe suite.

Ketergantungan yang dihasilkan sederhana: persiapan fixture selesai, inisialisasi selesai, lalu metode pengujian dimulai. Inisialisasi Anda harus benar-benar menunggu operasi persiapan dengan await. Sekadar memulai pekerjaan di dalamnya tidak membentuk ketergantungan tersebut.

Gunakan panduan keputusan berikut untuk memilih lokasi persiapan:

Situasi Lokasi persiapan Apa yang memastikan kesiapan?
Beberapa pengujian instance memerlukan resep fixture yang sama Suite init() async throws Inisialisasi yang ditunggu selesai sebelum setiap metode
Hanya satu pengujian memerlukan fixture Isi pengujian async throws try await eksplisit sebelum pernyataan
Pengujian yang sudah ada mewarisi XCTestCase setUp() async throws XCTest menyelesaikan penyiapan asinkron sebelum metode dipanggil
Pengujian Swift Testing bersifat static atau global Isi pengujian atau helper yang dipanggil secara eksplisit Pengujian secara eksplisit menunggu persiapan

Tabel ini menerapkan aturan siklus hidup yang didokumentasikan untuk masing-masing framework pada penempatan fixture. Secara khusus, inisialisasi suite adalah titik persiapan per instance; ini tidak berarti persiapan hanya dilakukan sekali untuk seluruh suite. Lihat aturan inisialisasi suite dan urutan penyiapan XCTest dari Apple.

Tambahkan contoh fixture asinkron lengkap di Xcode

Gunakan Xcode 16 atau versi lebih baru dengan dukungan Swift Testing yang disertakan. Proyek Swift mendokumentasikan bahwa Swift Testing disertakan bersama Swift 6 dan Xcode 16, serta tidak memerlukan dependensi paket tambahan. Lihat README Swift Testing resmi.

Di target pengujian unit yang sudah ada, pilih File > New > New File From Template, lalu pilih Swift Testing Unit Test. Ganti isi file yang dihasilkan dengan contoh lengkap di bawah. Bundel pengujian unit XCTest yang sudah ada juga dapat memuat pengujian Swift Testing; bundel terpisah tidak diperlukan. Langkah-langkah ini mengikuti panduan Apple Menambahkan pengujian ke proyek Xcode Anda.

import Testing

enum FixtureError: Error {
    case invalidInput
}

private actor SquareCalculator {
    let values: [Int]

    init(values: [Int]) {
        self.values = values
    }

    func squares() -> [Int] {
        values.map { $0 * $0 }
    }
}

struct CalculationFixture {
    let squares: [Int]
    let total: Int

    static func make(values: [Int]) async throws -> Self {
        guard !values.isEmpty,
              values.count <= 100,
              values.allSatisfy({ (0...100).contains($0) }) else {
            throw FixtureError.invalidInput
        }

        let calculator = SquareCalculator(values: values)
        let squares = await calculator.squares()

        return Self(
            squares: squares,
            total: squares.reduce(0, +)
        )
    }
}

@Suite
struct CalculationTests {
    let fixture: CalculationFixture

    init() async throws {
        fixture = try await CalculationFixture.make(values: [2, 4, 6])
    }

    @Test
    func squaresAreReady() {
        #expect(fixture.squares == [4, 16, 36])
    }

    @Test
    func totalIsReady() {
        #expect(fixture.total == 56)
    }
}

Contoh ini menggunakan actor lokal sungguhan untuk melakukan perhitungan. Pemanggilan metode terisolasinya dari luar actor memerlukan await. Tidak ada jeda, input acak, dependensi eksternal, atau operasi latar belakang yang harus selesai tepat pada waktunya. Output tetap deterministik, terlepas dari apakah eksekusi benar-benar mengalami suspensi.

Factory fixture menolak input kosong, lebih dari 100 nilai, dan nilai di luar 0...100. Batas ilustratif ini menjaga perhitungan tetap kecil: setiap kuadrat paling besar 10.000 dan totalnya paling besar 1.000.000. Nilai yang tidak valid menghasilkan error, alih-alih dihapus atau diganti secara diam-diam.

Untuk input yang diberikan, perhitungan yang diharapkan adalah 2² + 4² + 6² = 4 + 16 + 36 = 56. Inisialisasi menyimpan fixture yang sudah selesai ke properti let yang tidak opsional. Karena itu, metode pengujian tidak memerlukan penanda kesiapan maupun pembukaan nilai opsional.

Metode pengujian itu sendiri dapat tetap sinkron karena prasyarat asinkronnya sudah diselesaikan dalam inisialisasi. Jika pekerjaan asinkron dilakukan di dalam isi pengujian, pola yang didokumentasikan Apple adalah menandai fungsi tersebut sebagai async dan langsung menunggu operasinya. Lihat Menguji kode asinkron.

Perhitungan sintetis ini diperiksa secara lokal menggunakan Python. Swift tidak tersedia di lingkungan riset ini, sehingga contoh Swift telah ditinjau tetapi tidak dikompilasi atau dijalankan di sini. Hasil yang diharapkan di atas menjelaskan perhitungan contoh, bukan laporan hasil eksekusi pengujian Xcode.

Mengapa memulai task tidak membuat penyiapan selesai?

Kesalahan umum adalah menjalankan Task { ... } dari inisialisasi sinkron lalu langsung mengembalikan kendali. Inisialisasi itu selesai setelah memulai pekerjaan, tetapi pekerjaan tersebut mungkin masih berjalan saat metode pengujian dimulai.

Panduan bahasa Swift menjelaskan bahwa Task { ... } membuat task tak terstruktur. Handle yang dikembalikannya dapat digunakan untuk menunggu hasil melalui await handle.value. Membuang handle tersebut menghilangkan titik penantian eksplisit itu. Lihat bagian Konkurensi Tak Terstruktur dalam buku resmi Swift.

Untuk inisialisasi fixture, menunggu factory secara langsung dalam inisialisasi asinkron biasanya merupakan cara paling jelas untuk menyatakan ketergantungan tersebut. Jika helper yang sudah ada menjalankan pekerjaan secara internal dan mengembalikan kendali sebelum pekerjaan itu selesai, menunggu helper hanya menunggu sampai helper mengembalikan kendali. Ubah kontrak penyelesaiannya agar pengembalian kendali berarti fixture sudah siap, atau tampilkan hasil task yang dapat ditunggu oleh pemanggil.

Menambahkan jeda tetap tidak menyatakan ketergantungan tersebut. Cara ini hanya menunggu waktu berlalu, sehingga kesiapan bergantung pada apakah persiapan selesai dalam interval itu. Demikian pula, menjadikan metode pengujian async hanya memungkinkan pemanggilan asinkron; metode itu tetap harus menunggu operasi yang menjadi prasyaratnya.

Apa padanan penyiapan di XCTest?

XCTest menyediakan hook penyiapan asinkron khusus. Urutan yang didokumentasikannya sebelum setiap metode pengujian adalah setUp() async throws, lalu setUpWithError(), kemudian setUp() yang sinkron. Metode pengujian dijalankan setelah hook tersebut. Lihat Menyiapkan dan Membongkar State dalam Pengujian Anda.

Untuk membandingkan fixture yang sama di XCTest, tambahkan file pendamping lengkap berikut ke target pengujian unit yang sama dengan contoh pertama. File ini mengimpor XCTest dan menggunakan CalculationFixture yang telah didefinisikan sebelumnya dan tersedia pada tingkat modul. Pertahankan definisi helper di file pertama; jangan gandakan.

import XCTest

final class CalculationXCTests: XCTestCase {
    private var fixture: CalculationFixture?

    override func setUp() async throws {
        try await super.setUp()
        fixture = try await CalculationFixture.make(values: [2, 4, 6])
    }

    func testTotalIsReady() throws {
        let readyFixture = try XCTUnwrap(fixture)
        XCTAssertEqual(readyFixture.squares, [4, 16, 36])
        XCTAssertEqual(readyFixture.total, 56)
    }
}

Metode XCTest juga dapat bersifat sinkron: persiapan asinkron berlangsung dalam hook penyiapan. Properti opsional mengakomodasi fase penyiapan XCTest yang terpisah, dan XCTUnwrap menjadikan fixture yang tidak tersedia sebagai kegagalan eksplisit. Apple mendokumentasikan pola pembukaan nilai tersebut dalam panduan migrasinya.

Saat mengonversi pengujian ini ke Swift Testing, pindahkan persiapannya ke inisialisasi suite dan gunakan import Testing, @Test, serta ekspektasi Swift Testing. Metode yang sekadar bernama setUp di suite Swift Testing biasa bukan mekanisme penyiapan yang didokumentasikan; jalur migrasi Apple mengganti rangkaian metode penyiapan dengan inisialisasi.

Periksa batasan berikut jika inisialisasi masih tampak terlalu awal

Pertama, periksa apakah pengujian tersebut merupakan metode instance. Pengujian static tidak memerlukan instance suite, sehingga tidak dapat bergantung pada inisialisasi instance otomatis. Pengujian global juga tidak memiliki instance yang menampungnya. Untuk kedua bentuk tersebut, buat dan tunggu fixture di dalam isi pengujian. Bentuk pengujian yang didukung dijelaskan dalam Memigrasikan pengujian dari XCTest.

Kedua, periksa seluruh rantai pemanggilan persiapan. Temukan operasi yang benar-benar menghasilkan state siap, lalu pastikan setiap pemanggil menunggunya. Dalam contoh ini, perhitungan actor selesai sebelum factory mengembalikan kendali, dan factory mengembalikan kendali sebelum inisialisasi selesai. Tidak ada pekerjaan yang diperlukan oleh pernyataan pengujian yang keluar dari urutan tersebut.

Ketiga, biarkan error penyiapan diteruskan. Jika CalculationFixture.make melempar error, instance suite tidak dapat menyelesaikan inisialisasi, sehingga metode pengujian instance-nya tidak dapat berjalan pada instance itu. Hindari mengganti persiapan yang gagal dengan fixture kosong melalui try? dan nilai cadangan, kecuali perilaku tersebut memang sedang diuji secara eksplisit. Error input tidak valid dalam contoh ini sengaja mencegah fixture yang tampak dapat digunakan dibuat.

Terakhir, bedakan urutan inisialisasi dari penjadwalan pengujian. Secara default, Swift Testing menjalankan pengujian secara paralel. Trait .serialized pada suite dapat membuat pengujian di dalamnya berjalan berurutan, tetapi serialisasi tidak mengubah task yang tidak ditunggu menjadi persiapan yang ditunggu, dan juga tidak membuat inisialisasi suite hanya berjalan sekali. Apple mendokumentasikan cakupan trait tersebut dalam Menjalankan pengujian secara serial atau paralel.

Untuk perhitungan lokal ini, pertahankan fixture agar tidak dapat diubah dan siapkan secara terpisah untuk setiap pengujian instance. Pemeriksaan utamanya adalah rantai ketergantungan: operasi persiapan mengembalikan fixture yang telah selesai, inisialisasi menunggu hasil tersebut, lalu pengujian membaca properti yang sudah diinisialisasi. Dengan begitu, pengujian memiliki kondisi kesiapan yang konkret tanpa bergantung pada asumsi waktu.

Sumber