Verplaats een op lussen gebaseerde XCTest-unit-test naar de geparametriseerde @Test van Swift Testing om elk scenario een eigen resultaat en mogelijkheid om het opnieuw uit te voeren in Xcode te geven. Deze handleiding gebruikt een lokale berekening voor de tegelindeling om de migratie te laten zien en het rapportagegedrag ervan te controleren.
Waarom rapporteert een XCTest-lus niet elk scenario afzonderlijk?
XCTest vindt testmethoden op een subklasse van XCTestCase. Deze methoden hebben geen parameters en hun namen beginnen met test. Een lus binnen één gevonden methode blijft onderdeel van die methode; de iteraties worden geen afzonderlijk gevonden tests. Een scenarionaam toevoegen aan een assertion kan de diagnostiek verbeteren, maar creëert geen afzonderlijke mogelijkheid om de test uit te voeren. Zie Apples Defining Test Cases and Test Methods.
De levenscyclus volgt dezelfde grens. XCTest voert de instantiesetupmethoden uit vóór elke testmethode en de teardownmethoden erna. Een lus met vijf iteraties krijgt dus niet automatisch vijf setup- en teardowcycli. Als een iteratie een fixture wijzigt, kan de volgende iteratie die toestand overnemen, tenzij de test deze expliciet reset. Apple beschrijft deze volgorde in Set Up and Tear Down State in Your Tests.
Swift Testing biedt hier de gewenste uitkomst: één functie accepteert scenarioargumenten en de runner behandelt de aanroepen als onafhankelijke gevallen. Xcode toont de argumenten en biedt afzonderlijke uitvoeropties. Apple demonstreert dit gedrag in Go further with Swift Testing.
Het onderscheid is van belang bij het diagnosticeren van fouten. Een assertionfout en een voortijdige beëindiging zijn verschillende gebeurtenissen: een lus kan na een gewone assertion doorgaan met controleren, terwijl een gegooide fout of expliciete return kan verhinderen dat latere iteraties worden uitgevoerd. Door scenario's in afzonderlijke gevallen op te splitsen, verdwijnt die gedeelde control flow tussen scenario's. Een fout die het proces beëindigt, wordt daardoor niet herstelbaar.
Bereid het bestaande unit-testdoel voor
Gebruik Xcode 16 of hoger voor deze workflow. Swift Testing is opgenomen in die Xcode-distributies en in Swift.org-toolchains vanaf Swift 6.0. Het Swift-project raadt aan voor normaal testwerk de ingebouwde versie te gebruiken; voor dit voorbeeld is geen externe pakketafhankelijkheid nodig. Zie Obtaining Swift Testing.
Behoud de bestaande unit-testbundel. Apple ondersteunt Swift Testing en XCTest expliciet in hetzelfde doel, zodat een geleidelijke migratie kan beginnen met één nieuw Swift-bestand. Kies in Xcode File > New > New File From Template, selecteer Swift Testing Unit Test en voeg het bestand toe aan het bestaande unit-testdoel. Als het project geen testdoel heeft, maak je via File > New > Target een Unit Testing Bundle aan. Deze stappen staan beschreven in Adding tests to your Xcode project.
Deze handleiding test rechtstreeks berekeningscode. Er wordt geen desktopvenster geautomatiseerd. Apples gedocumenteerde aanpak voor UI-automatisering blijft XCTest gebruiken; pas deze geparametriseerde aanpak toe op de onderliggende logica die de indeling bepaalt. Dezelfde handleiding voor het toevoegen van tests beschrijft die grens tussen frameworks.
Maak van elk scenario een volledig argumentrecord
Stel dat een Mac-app tegels in rijen rangschikt. Met vier kolommen zijn er nul rijen nodig voor nul tegels, vullen vier tegels één rij en zijn er voor vijf tegels twee rijen nodig. De concrete taak is om die controles te behouden en elk scenario afzonderlijk zichtbaar en uitvoerbaar te maken.
Gebruik één record met de scenarionaam, invoerwaarden en verwachte uitvoer. Zo blijft het verwachte resultaat gekoppeld aan de invoer wanneer gevallen worden toegevoegd of opnieuw gerangschikt. De volgende vijf records zijn illustratieve testgegevens, geen waarnemingen uit een bestaande applicatie.
| Scenario | Aantal tegels | Kolommen | Verwachte rijen | Doel |
|---|---|---|---|---|
empty |
0 | 4 | 0 | Lege invoer |
one-tile |
1 | 4 | 1 | Gedeeltelijk gevulde eerste rij |
exact-row |
4 | 4 | 1 | Exacte grens |
wrap |
5 | 4 | 2 | Eerste tegel voorbij de grens |
two-full-rows |
8 | 4 | 2 | Exact veelvoud |
Plaats dit volledige, zelfstandige bestand in het unit-testdoel. Het bevat een synthetische implementatie, zodat je het kunt uitproberen zonder appmodule of andere bestanden. Het gebruikt geen services, mocks, bestanden of netwerktoegang.
import Testing
private struct TileLayout {
func rowCount(tileCount: Int, columns: Int) -> Int {
precondition(tileCount >= 0)
precondition(columns > 0)
let fullRows = tileCount / columns
let partialRow = tileCount % columns == 0 ? 0 : 1
return fullRows + partialRow
}
}
private struct LayoutScenario:
Codable, Sendable, CustomTestStringConvertible {
let name: String
let tileCount: Int
let columns: Int
let expectedRows: Int
var testDescription: String { name }
static let examples: [LayoutScenario] = [
.init(name: "empty", tileCount: 0,
columns: 4, expectedRows: 0),
.init(name: "one-tile", tileCount: 1,
columns: 4, expectedRows: 1),
.init(name: "exact-row", tileCount: 4,
columns: 4, expectedRows: 1),
.init(name: "wrap", tileCount: 5,
columns: 4, expectedRows: 2),
.init(name: "two-full-rows", tileCount: 8,
columns: 4, expectedRows: 2)
]
}
struct TileLayoutTests {
@Test("Tile rows", arguments: LayoutScenario.examples)
fileprivate func rowCountMatchesScenario(
_ scenario: LayoutScenario
) {
let layout = TileLayout()
let actualRows = layout.rowCount(
tileCount: scenario.tileCount,
columns: scenario.columns
)
#expect(actualRows == scenario.expectedRows)
}
}
De migratie verplaatst de iteratie naar @Test(arguments:): elk element wordt het argument voor één aanroep. Codable biedt ondersteuning voor codering bij het selectief opnieuw uitvoeren, terwijl CustomTestStringConvertible het leesbare scenariolabel levert. Apple beschrijft parameterisering in Implementing parameterized tests en aangepaste argumentbeschrijvingen in Go further with Swift Testing.
De fixture wordt bewust binnen de testfunctie gemaakt. Elke aanroep maakt een eigen TileLayout; niets is afhankelijk van een eerder scenario. Als je een bestaande fixture migreert, verplaats de setup dan bewust in plaats van ervan uit te gaan dat overgenomen XCTest-hooks worden uitgevoerd. Swift Testing-suites erven niet van XCTestCase; Apple vertaalt setup naar een initializer en class- of actor-teardown naar deinit in Migrating a test from XCTest.
De berekening accepteert niet-negatieve aantallen tegels en strikt positieve aantallen kolommen. De preconditions leggen dat contract vast; ongeldige waarden vallen buiten de gevallen in dit voorbeeld. Als echte applicatiecode een herstelbare fout moet retourneren bij ongeldige invoer, wijzig dan dat API-contract en voeg de bijbehorende tests toe voordat je deze tabel uitbreidt.
Bevestig afzonderlijke resultaten en voer een mislukt geval opnieuw uit
Voer de geparametriseerde functie uit met het ruitje naast de declaratie. Open vervolgens de Test navigator en vouw de functie uit om de gevallen te bekijken. Als je één geval wilt uitvoeren, wijs je naar het ruitje van dat geval en klik je op de afspeelknop. Met de afspeelknop van de bovenliggende functie voer je alle gevallen uit. Apple beschrijft beide handelingen in Running tests and interpreting results.
Wijzig voor een gecontroleerde rapportagecontrole tijdelijk de waarde expectedRows van het record wrap van 2 in 3. Voer de hele geparametriseerde functie uit. Het verwachte resultaat is vijf uitgevoerde gevallen, waarbij de vergelijking wrap mislukt omdat de implementatie 2 retourneert. Selecteer dat geval en voer het afzonderlijk opnieuw uit; herstel daarna de verwachte waarde naar 2. Dit is een bewuste wijziging van testgegevens, geen bewijs van een implementatiefout.
Bekijk de details Results en Arguments van de mislukte verwachting om de berekende waarde met de scenario-invoer te vergelijken. Open voor de volledige uitvoering View > Navigators > Reports, selecteer de testactie en bekijk de sectie Tests. Deze bedieningselementen voor inspectie worden beschreven in Apples handleiding voor testresultaten.
Selectief uitvoeren vereist meer dan leesbare labels: de runner moet elk argument deterministisch kunnen koppelen. Volgens Apples huidige documentatie worden CustomTestArgumentEncodable, bepaalde typen RawRepresentable, Encodable of Identifiable met een codeerbare identifier geaccepteerd. Het Codable-record van het voorbeeld voldoet aan de codeerroute. Een aangepaste weergavestring alleen voldoet niet aan die vereiste. Zie Implementing parameterized tests.
De verwachte rekenresultaten voor alle vijf records zijn lokaal gecontroleerd met Python-gehele deling en rest. Een illustratieve onjuiste implementatie die alleen gehele deling naar beneden gebruikt, slaagt niet voor one-tile en wrap. Het Swift-bestand is bekeken maar niet gecompileerd in deze onderzoeksomgeving, omdat Swift niet beschikbaar was; de Xcode-uitvoering hierboven blijft de uitvoerbare verificatiestap.
Behoud het beoogde aantal gevallen en de levenscyclus
Een nuttige migratiecontrole vergelijkt drie dingen: het aantal scenariorecords, de uitgevoerde gevallen in de volledige run en of een geselecteerd geval afzonderlijk kan worden uitgevoerd. Voor dit voorbeeld is het beoogde aantal vijf. Een ander aantal moet aanleiding zijn om de argumentconstructie en testselectie te onderzoeken voordat je de berekening wijzigt.
Houd bijbehorende verwachtingen bij elkaar. Door twee afzonderlijke verzamelingen aan arguments: door te geven, ontstaat hun cartesisch product. Vijf invoerwaarden en vijf verwachte uitvoerwaarden leveren dus 25 combinaties op in plaats van vijf bij elkaar horende scenario's. Gebruik afzonderlijke verzamelingen wanneer elke combinatie betekenisvol is; gebruik records of zip() voor gekoppelde gegevens. Apple legt het verschil uit in Implementing parameterized tests.
Als je zip() gebruikt, controleer dan de lengtes van de verzamelingen voordat je de paren maakt: Swift stopt bij de kortste sequentie. Een extra verwacht resultaat kan anders ongebruikt blijven zonder dat er een testgeval voor wordt aangemaakt. Eén scenariorecord voorkomt die specifieke mismatch. Het afkapgedrag staat beschreven in Swift-referentie voor zip(::).
Ook de uitvoeringsvolgorde beïnvloedt de migratiekeuze. Swift Testing voert gevallen standaard parallel uit. Dit voorbeeld gebruikt onveranderlijke argumenten en een lokale fixture, dus de resultaten zijn niet afhankelijk van de volgorde. Voeg voor een geparametriseerde functie die tijdelijk serieel moet worden uitgevoerd .serialized toe aan het kenmerk @Test. Als je die trait op een suite toepast, worden de tests en gevallen daarin ook serieel uitgevoerd, maar niet de tests in andere suites. Zie Running tests serially or in parallel.
Seriële uitvoering maakt geen nieuwe toestand aan en legt geen afhankelijkheidscontract tussen scenario's vast. Als een geval alleen slaagt nadat een ander geval iets heeft gewijzigd, pas dan de opzet aan zodat het zelfstandig kan worden uitgevoerd. Als de stappen bewust één doorlopende bewerking vormen, behoud ze dan binnen één test en parameteriseer volledige bewerkingen.
Gebruik in dit gemigreerde voorbeeld overal assertions van Swift Testing. Controleer bij hergebruik van helpers met XCTest-assertions hoe de toolchain met interoperabiliteit omgaat: Apples huidige migratiehandleiding beschrijft modi en standaardinstellingen die per toolchain en versie van de package tools verschillen. Door dit bestand volledig binnen Testing te houden, vermijd je die afhankelijkheid. Zie Migrating a test from XCTest.
Als de migratie moet wachten, is het ondersteunde alternatief in XCTest één benoemde, parameterloze testmethode per scenario, met waar nodig gedeelde berekenings- en assertionhelpers. Apple raadt aan voor elk scenario een test te schrijven in de handleiding voor het toevoegen van tests. Zo behoud je afzonderlijke methoderesultaten zonder parameterisering van Swift Testing.
De omzetting is voltooid wanneer elk oorspronkelijk scenario overeenkomt met een uitgevoerd geval, de fixture begint in de bedoelde toestand en een geselecteerd geval zelfstandig succesvol kan worden uitgevoerd. Behoud bij die wijziging de grensinvoer en verwachte uitvoer: onafhankelijke rapportage is nuttig omdat je die bestaande controles zo makkelijker kunt vinden, diagnosticeren en herhalen.
Bronnen
- Defining Test Cases and Test Methods — Apple
- Set Up and Tear Down State in Your Tests — Apple
- Go further with Swift Testing — Apple
- Obtaining Swift Testing — Swift-project
- Adding tests to your Xcode project — Apple
- Implementing parameterized tests — Apple
- Migrating a test from XCTest — Apple
- Running tests and interpreting results — Apple
- zip(::) — Apple
- Running tests serially or in parallel — Apple