Untuk memastikan kes XCTest lulus secara individu dan sebagai satu suite, berikan setiap ujian fixture boleh ubahnya sendiri dan suntikkannya ke dalam kod yang diuji. Apabila singleton perlu kekal dikongsi, tetapkan semula sebelum setiap ujian dan cegah akses bertindih sepanjang tempoh hayat ujian itu.
Panduan ini mengikuti satu tugasan pembangunan Mac: membetulkan ujian pengiraan susun atur dalam Xcode yang lulus secara berasingan tetapi gagal apabila dijalankan bersama. Contoh ini hanya menggunakan lebar integer sintetik dan aritmetik setempat. Ia tidak memerlukan tetingkap aplikasi, mock perkhidmatan, storan fail atau sambungan rangkaian.
Mengapa ujian boleh lulus secara berasingan tetapi gagal dalam suite?
Ujian yang dijalankan secara berasingan mungkin menemui keadaan awal singleton. Dalam satu suite, ujian terdahulu mungkin sudah mengubahnya. Rujukan baharu kepada singleton itu tetap merujuk kepada objek boleh ubah yang sama.
Swift membezakan sifat instance daripada sifat jenis: setiap instance mempunyai sifat instance tersendiri, manakala sifat jenis mempunyai satu salinan yang dikongsi. Sifat jenis tersimpan dimulakan sekali pada akses pertama. Oleh itu, menetapkan SpacingCalculator.shared kepada sifat ujian tidak menghasilkan kalkulator baharu. Lihat dokumentasi Properties projek Swift.
Pertimbangkan satu baris sintetik yang mengandungi item dengan lebar [10, 20, 30]. Jumlah lebarnya ialah hasil tambah lebar item dengan jurang di antaranya. Dengan jurang lalai 2, hasilnya ialah 64; dengan jurang 8, hasilnya ialah 76.
Katakan satu ujian memeriksa lebar lalai tanpa mengkonfigurasi kalkulator, manakala ujian lain menukar jurang yang dikongsi kepada 8. Jadual berikut menerangkan pelaksanaan berjujukan, dengan setiap baris bermula menggunakan kalkulator baharu yang jurangnya 2. Ini ialah pengiraan ilustrasi, bukannya keputusan ujian Xcode yang direkodkan.
| Pelaksanaan | Kalkulator dikongsi tanpa tetapan semula | Kalkulator baharu bagi setiap ujian |
|---|---|---|
| Ujian lalai sahaja | 64: lulus |
64: lulus |
| Ujian jurang lebar sahaja | 76: lulus |
76: lulus |
| Lalai, kemudian jurang lebar | Kedua-duanya lulus | Kedua-duanya lulus |
| Jurang lebar, kemudian lalai | Ujian lalai menerima 76: gagal |
Kedua-duanya lulus |
Ini memberikan petunjuk diagnostik yang berguna: periksa ujian yang mengubah keadaan yang digunakan oleh asersi yang gagal. Menukar nama ujian untuk mendapatkan susunan yang menguntungkan hanya mengekalkan kebergantungan itu.
Kaedah kitar hayat XCTest yang manakah patut memiliki fixture?
Gunakan kaedah persediaan instance untuk keadaan boleh ubah yang diperlukan oleh setiap ujian. Apple mendokumenkan bahawa persediaan instance dijalankan sebelum setiap kaedah ujian, manakala persediaan peringkat kelas dijalankan sekali sebelum kelas ujian. Oleh itu, fixture boleh ubah yang dicipta dalam persediaan peringkat kelas tidak mendapat garis dasar baharu antara kaedah. Lihat Sediakan dan Bersihkan Keadaan dalam Ujian Anda.
Pilih kaedah persediaan yang sepadan dengan kerja: pembinaan fixture segerak yang tidak melemparkan ralat sesuai untuk setUp(); pembinaan yang melemparkan ralat sesuai untuk setUpWithError(); persediaan tak segerak sesuai untuk setUp() async throws. Gunakan teardown untuk menyelesaikan pembersihan ujian semasa. Apple secara jelas menasihatkan supaya tidak bergantung pada teardown untuk menyediakan ujian seterusnya, kerana ranap boleh menghalang teardown daripada dijalankan. Dokumentasi kitar hayat yang sama menerangkan sempadan ini.
Keputusan tentang fixture bergantung pada pemilikan dan skop pelaksanaan:
| Situasi | Pendekatan yang sesuai | Syarat yang perlu diperiksa |
|---|---|---|
| Ujian boleh membina kebergantungan | Cipta dan suntik satu instance bagi setiap ujian | Pengguna mesti menggunakan instance yang disuntik itu |
| Ujian mesti menguji singleton sebenar | Tetapkan garis dasar lengkap sebelum setiap ujian | Tiada ujian bertindih boleh mengubah singleton itu |
| Konfigurasi dikongsi tidak boleh diubah | Gunakan semula nilai tidak boleh ubah itu | Objek yang dirujuk juga mesti kekal tidak berubah |
| Ujian memulakan kerja tak segerak | Tunggu sehingga selesai sebelum ujian berakhir | Tiada kerja belum selesai boleh mengubah fixture selepas itu |
Pilihan pertama ialah yang paling mudah untuk kalkulator ini. Kod produksi boleh mengekalkan lalai dikongsi sementara ujian memilih instance bebas secara jelas.
Bina fixture XCTest yang dimiliki secara berasingan
Gunakan Mac dengan Xcode dan sasaran ujian unit macOS sedia ada. Tambahkan contoh lengkap berikut sebagai satu fail Swift dalam sasaran ujian itu. Ia mentakrifkan kalkulator, pengguna dan ujian sendiri, jadi tidak memerlukan import modul aplikasi. Gunakan toolchain Swift 6 dengan pengasingan aktor lalai sasaran ditetapkan kepada nonisolated.
Kalkulator ini ialah aktor untuk menjadikan sempadan keserentakan keadaan boleh ubahnya jelas. Aktor Swift mengehadkan akses kepada keadaan boleh ubahnya, dan panggilan dari luar aktor menggunakan await. Peraturan ini didokumenkan dalam bab Concurrency projek Swift. Pemilikan fixture secara berasingan tetap diperlukan walaupun menggunakan aktor.
import XCTest
actor SpacingCalculator {
static let shared = SpacingCalculator()
private var gap = 2
func setGap(_ value: Int) {
precondition(value >= 0)
gap = value
}
func width(of itemWidths: [Int]) -> Int {
precondition(itemWidths.allSatisfy { $0 >= 0 })
let gapCount = max(itemWidths.count - 1, 0)
return itemWidths.reduce(0, +) + gapCount * gap
}
}
struct RowLayout {
let calculator: SpacingCalculator
init(calculator: SpacingCalculator = .shared) {
self.calculator = calculator
}
func width(of itemWidths: [Int]) async -> Int {
await calculator.width(of: itemWidths)
}
}
final class RowLayoutTests: XCTestCase {
private var calculator: SpacingCalculator?
override func setUp() {
super.setUp()
calculator = SpacingCalculator()
}
override func tearDown() {
calculator = nil
super.tearDown()
}
func testDefaultGap() async throws {
let fixture = try XCTUnwrap(calculator)
let layout = RowLayout(calculator: fixture)
let actual = await layout.width(of: [10, 20, 30])
XCTAssertEqual(actual, 64)
}
func testWideGap() async throws {
let fixture = try XCTUnwrap(calculator)
await fixture.setGap(8)
let layout = RowLayout(calculator: fixture)
let actual = await layout.width(of: [10, 20, 30])
XCTAssertEqual(actual, 76)
}
func testEmptyRow() async throws {
let fixture = try XCTUnwrap(calculator)
let layout = RowLayout(calculator: fixture)
let actual = await layout.width(of: [])
XCTAssertEqual(actual, 0)
}
func testSingleItem() async throws {
let fixture = try XCTUnwrap(calculator)
let layout = RowLayout(calculator: fixture)
let actual = await layout.width(of: [10])
XCTAssertEqual(actual, 10)
}
}
Baris yang menentukan ialah calculator = SpacingCalculator(). Setiap persediaan membina aktor bebas dengan jurang 2. Kemudian setiap ujian menghantar aktor itu kepada RowLayout. Memanggil RowLayout() dalam ujian ini akan memilih .shared dan memintas fixture.
Contoh ini sengaja membenarkan pembinaan secara langsung. Jika singleton sedia ada anda mempunyai pemula peribadi, perkenalkan laluan pembinaan dalaman atau asingkan keadaan pengiraan boleh ubahnya menjadi kebergantungan yang boleh dibina secara berasingan. Pemula dalaman boleh didedahkan kepada sasaran ujian melalui aturan import testable modul aplikasi yang sedia ada; menjadikan rujukan singleton boleh diganti secara global tidak diperlukan.
Aritmetik ini turut mentakrifkan dua kes pinggir yang berguna: baris kosong mempunyai lebar 0, dan satu item tidak mempunyai jurang di antaranya. Contoh ini menerima integer bukan negatif yang cukup kecil untuk mengelakkan limpahan integer; nilai negatif melanggar prasyaratnya dan menamatkan pelaksanaan, bukannya menghasilkan kegagalan asersi.
Jangkaan aritmetik dan kedua-dua susunan dua ujian telah diperiksa secara setempat menggunakan pengiraan Python deterministik. Contoh Swift telah diperiksa tetapi tidak dikompil atau dilaksanakan kerana persekitaran penyelidikan ini tidak mempunyai toolchain Swift. Jalankannya dalam Xcode untuk mengesahkan kompilasi dan pelaksanaan bagi sasaran anda.
Bilakah menetapkan semula singleton sudah memadai?
Menetapkan semula ialah pendekatan peralihan yang boleh digunakan apabila ujian benar-benar memerlukan instance dikongsi dan seluruh keadaan yang kelihatan kepada ujian boleh dipulihkan. Tetapkan garis dasar sebelum setiap ujian, termasuk nilai yang dianggap sebagai lalai oleh ujian. Menetapkan semula hanya sifat yang diubah oleh ujian yang sedang gagal boleh menyebabkan kebergantungan lain tidak dikenal pasti.
Bagi kalkulator ini, persediaan tak segerak boleh memilih SpacingCalculator.shared dan menunggu setGap(2) sebelum setiap kaedah. Ini menetapkan garis dasar aritmetik hanya jika tiada pemanggil bertindih mengubah jurang sebelum asersi. Jika fixture kemudian menambah hasil cache atau konfigurasi lain, operasi garis dasarnya juga mesti meliputi nilai tersebut.
Aktor tidak memberikan pemilikan kepada satu ujian merentasi beberapa panggilan yang ditunggu. Contohnya, ujian A boleh menetapkan jurang dikongsi kepada 2, ujian B boleh menetapkannya kepada 8, kemudian ujian A boleh mengira lebar menggunakan 8. Setiap operasi aktor boleh dilaksanakan dengan betul walaupun andaian keseluruhan ujian gagal. Hal ini berpunca daripada peraturan pelaksanaan dan penggantungan aktor yang didokumenkan oleh Swift.
Sebelum memilih pengasingan berasaskan tetapan semula, kenal pasti setiap ujian dan tugasan latar belakang yang boleh mencapai objek dikongsi itu. Jika anda tidak dapat memastikan penggunaan eksklusif sepanjang persediaan, pelaksanaan dan pembersihan, suntik keadaan bebas sebagai gantinya. Memulihkan petikan keadaan selepas itu tidak dapat menghalang pemanggil lain daripada melihat perubahan sementara semasa ujian.
Bagaimanakah fixture diterjemahkan kepada Swift Testing?
Swift Testing memulakan instance suite yang berbeza untuk setiap fungsi ujian instance. Oleh itu, sifat instance yang membina kalkulator memberikan corak pemilikan yang sama tanpa penggantian persediaan XCTest. Sifat yang dimulakan daripada .shared tetap merujuk kepada singleton. Apple mendokumenkan kitar hayat instance dalam Menyusun fungsi ujian dengan jenis suite.
Dengan takrifan SpacingCalculator dan RowLayout terdahulu hadir sekali dalam sasaran ujian yang sama, ini ialah suite Swift Testing tambahan yang lengkap. Ia memerlukan toolchain yang menyertakan modul Testing, seperti Xcode 16 atau lebih baharu. Pengasingan aktor lalainya juga patut ditetapkan kepada nonisolated.
import Testing
struct RowLayoutSwiftTests {
let calculator = SpacingCalculator()
@Test func defaultGap() async {
let layout = RowLayout(calculator: calculator)
let actual = await layout.width(of: [10, 20, 30])
#expect(actual == 64)
}
@Test func wideGap() async {
await calculator.setGap(8)
let layout = RowLayout(calculator: calculator)
let actual = await layout.width(of: [10, 20, 30])
#expect(actual == 76)
}
}
Untuk persediaan yang lebih rumit, Swift Testing menyokong pemula suite, termasuk pemula tak segerak atau yang melemparkan ralat. Memindahkan ujian daripada XCTest oleh Apple menerangkan penggantian ini bagi kaedah persediaan XCTest. Fixture ini tidak memerlukan pembersihan eksplisit kerana ia hanya memiliki keadaan pengiraan setempat dan tidak memulakan kerja latar belakang.
Swift Testing menjalankan ujian secara selari secara lalai. Menggunakan .serialized pada suite akan menjalankan ujian terkandung dan suite bersarang secara bersiri, tetapi tidak menghalang ujian yang tidak berkaitan daripada berjalan serentak dengannya. Menggunakan trait itu pada fungsi ujian biasa yang bukan berparameter tidak memberi kesan. Had ini dinyatakan dengan jelas dalam Menjalankan ujian secara bersiri atau selari.
Oleh itu, meletakkan dua suite yang bergantung pada singleton di bawah anotasi .serialized yang berasingan tidak mewujudkan pengecualian bersama antara kedua-duanya. Induk bersiri yang sama boleh menyelaraskan keturunannya, dengan syarat setiap pengguna yang bersaing berada dalam skop itu. Fixture baharu mengelakkan keperluan penyelarasan tersebut untuk ujian pengiraan ini.
Sahkan hasil ujian individu dan suite dalam Xcode
Gunakan skema, pelan ujian dan destinasi Mac yang sama sepanjang pengesahan. Apple mendokumenkan pelaksanaan kaedah individu, suite dan pelan ujian aktif dalam Menjalankan ujian dan mentafsir hasil.
- Jalankan setiap satu daripada empat kaedah XCTest secara individu menggunakan kawalan main di tepi editor. Jangkakan
64,76,0dan10, masing-masing. - Jalankan kelas
RowLayoutTestsmenggunakan kawalan main di sebelah pengisytiharannya. Keempat-empat kaedah sepatutnya lulus bersama-sama. - Pilih
Product > Testuntuk menjalankan pelan ujian aktif. Sahkan bahawa ujian ini benar-benar dijalankan, bukannya dilangkau atau dikecualikan. - Jika anda menambah contoh Swift Testing, jalankan kedua-dua fungsinya secara individu dan bersama-sama juga.
Jika ujian individu lulus tetapi pelan penuh gagal, jejaki objek yang digunakan oleh pengguna yang gagal itu. Cari suntikan yang terlepas, pembantu yang memanggil .shared atau kerja tak segerak yang berterusan selepas ujian asalnya. Jika ujian gagal secara berasingan, periksa input dan pengiraan yang dijangka sebelum mengaitkan kegagalan itu dengan susunan suite.
Fixture yang siap sepatutnya menjadikan keadaan permulaannya jelas dalam laluan pembinaan ujian semasa. Bagi pengiraan susun atur ini, setiap ujian memiliki kalkulator, membekalkannya secara jelas kepada pengguna dan menunggu kerjanya. Ini menjadikan hasil yang dijangka bebas daripada ujian jiran yang dijalankan terlebih dahulu.
Sumber
- Sediakan dan Bersihkan Keadaan dalam Ujian Anda — Apple
- Properties — The Swift Programming Language, projek Swift
- Concurrency — The Swift Programming Language, projek Swift
- Menyusun fungsi ujian dengan jenis suite — Apple
- Memindahkan ujian daripada XCTest — Apple
- Menjalankan ujian secara bersiri atau selari — Apple
- Menjalankan ujian dan mentafsir hasil — Apple