Om du vill ge varje scenario ett eget resultat och möjlighet att köra om det i Xcode flyttar du ett loopbaserat XCTest-enhetstest till Swift Testings parameteriserade @Test. Den här genomgången använder en lokal beräkning av en plattlayout för att visa migreringen och verifiera hur resultaten rapporteras.
Varför rapporterar inte en XCTest-loop varje scenario separat?
XCTest hittar testmetoder i en underklass till XCTestCase. Metoderna tar inga parametrar och har namn som börjar med test. En loop i en hittad metod förblir en del av den metoden; loopens varv blir inte separata upptäckta tester. Om du lägger till ett scenarionamn i ett påstående kan det förbättra diagnostiken, men det skapar ingen separat körkontroll. Se Apples Defining Test Cases and Test Methods.
Livscykeln följer samma gräns. XCTest kör metodens inställningsmetoder före varje testmetod och dess nedmonteringsmetoder efteråt. En loop med fem varv får därför inte automatiskt fem cykler för inställning och nedmontering. Om ett varv ändrar ett testobjekt kan nästa varv ärva det tillståndet om testet inte återställer det uttryckligen. Apple beskriver förloppet i Set Up and Tear Down State in Your Tests.
Swift Testing erbjuder det resultat som behövs här: en funktion tar emot scenarioargument, och testköraren behandlar dess anrop som oberoende testfall. Xcode visar argumenten och erbjuder separata körkontroller. Apple visar detta beteende i Go further with Swift Testing.
Skillnaden spelar roll när fel ska diagnostiseras. Ett påstående som fallerar och ett förtida avslut är olika händelser: en loop kan fortsätta kontrollera efter ett vanligt påstående, medan ett kastat fel eller en uttrycklig return kan hindra senare varv från att köras. Om scenarierna delas upp i testfall försvinner det gemensamma kontrollflödet mellan dem. Det gör inte ett fel som avslutar processen möjligt att återhämta sig från.
Förbered det befintliga enhetstestmålet
Använd Xcode 16 eller senare för det här arbetsflödet. Swift Testing ingår i de Xcode-versionerna och i Swift.orgs verktygskedjor från och med Swift 6.0. Swift-projektet rekommenderar att använda den inbyggda kopian för vanligt testarbete; exemplet kräver inget externt paketberoende. Se Obtaining Swift Testing.
Behåll det befintliga enhetstestpaketet. Apple stöder uttryckligen Swift Testing och XCTest i samma mål, så en stegvis migrering kan börja med en ny Swift-fil. I Xcode väljer du File > New > New File From Template, väljer Swift Testing Unit Test och lägger till filen i det befintliga enhetstestmålet. Om projektet saknar ett testmål skapar du ett Unit Testing Bundle via File > New > Target. Stegen beskrivs i Adding tests to your Xcode project.
Den här genomgången testar beräkningskod direkt. Den automatiserar inte ett skrivbordsfönster. Apples dokumenterade väg för UI-automatisering använder fortfarande XCTest; använd det här parameteriserade tillvägagångssättet för den underliggande logiken som bestämmer layouten. Samma guide för att lägga till tester beskriver den gränsen mellan ramverken.
Gör varje scenario till en komplett argumentpost
Anta att en Mac-app ordnar plattor i rader. Med fyra kolumner kräver noll plattor noll rader, fyra plattor fyller en rad och fem plattor kräver två rader. Den konkreta uppgiften är att bevara dessa kontroller samtidigt som varje scenario blir synligt och körbart för sig.
Använd en post som innehåller scenarionamn, indata och förväntat resultat. Då förblir det förväntade resultatet kopplat till sin indata när testfall läggs till eller sorteras om. Följande fem poster är illustrativa testdata, inte observationer från ett befintligt program.
| Scenario | Antal plattor | Kolumner | Förväntade rader | Syfte |
|---|---|---|---|---|
empty |
0 | 4 | 0 | Tom indata |
one-tile |
1 | 4 | 1 | Delvis fylld första rad |
exact-row |
4 | 4 | 1 | Exakt gräns |
wrap |
5 | 4 | 2 | Första plattan utöver gränsen |
two-full-rows |
8 | 4 | 2 | Exakt multipel |
Placera den här kompletta, självständiga filen i enhetstestmålet. Den innehåller en syntetisk implementation så att den kan provas utan en appmodul eller andra filer. Den använder inga tjänster, mockar, filer eller nätverksåtkomst.
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)
}
}
Migreringen flyttar iterationen till @Test(arguments:): varje element blir argumentet till ett anrop. Codable tillhandahåller stöd för kodning vid selektiva omkörningar, medan CustomTestStringConvertible tillhandahåller en lättläst scenariobeteckning. Apple beskriver parameterisering i Implementing parameterized tests och anpassade argumentbeskrivningar i Go further with Swift Testing.
Testobjektet skapas avsiktligt inuti testfunktionen. Varje anrop skapar en egen TileLayout; inget beror på ett tidigare scenario. Om du migrerar ett befintligt testobjekt ska du flytta dess inställning medvetet i stället för att förvänta dig att XCTest-hookar ärvs. Swift Testing-sviter ärver inte från XCTestCase; Apple mappar inställningen till en initializer och nedmonteringen för en klass eller aktör till deinit i Migrating a test from XCTest.
Beräkningen accepterar icke-negativa antal plattor och strikt positiva antal kolumner. Dess förvillkor definierar det kontraktet; ogiltiga värden ingår inte bland exemplens testfall. Om verklig programkod måste returnera ett återställningsbart fel för ogiltig indata ska du ändra API-kontraktet och lägga till motsvarande tester innan du utökar tabellen.
Bekräfta separata resultat och kör om ett enskilt felande testfall
Kör den parameteriserade funktionen med hjälp av diamanten bredvid dess deklaration. Öppna sedan Test navigator och expandera funktionen för att granska dess testfall. Om du vill köra ett testfall pekar du på dess diamant och klickar på uppspelningskontrollen. Om du klickar på uppspelningskontrollen för den överordnade funktionen körs alla dess testfall. Apple beskriver båda åtgärderna i Running tests and interpreting results.
Gör en kontrollerad rapporteringskontroll genom att tillfälligt ändra wrap-postens expectedRows från 2 till 3. Kör hela den parameteriserade funktionen. Det förväntade resultatet är fem testkörningar, där jämförelsen i wrap fallerar eftersom implementationen returnerar 2. Markera det testfallet och kör om det separat. Återställ sedan det förväntade värdet till 2. Det här är en avsiktlig ändring av testdata, inte bevis på ett implementationsfel.
Granska den felande förväntningens detaljer i Results och Arguments för att jämföra det beräknade värdet med scenariots indata. Öppna View > Navigators > Reports för hela körningen, välj teståtgärden och granska avsnittet Tests. Dessa granskningskontroller beskrivs i Apples guide till testresultat.
Selektiv körning har ytterligare ett krav utöver lättlästa beteckningar: testköraren måste kunna matcha varje argument deterministiskt. Apples aktuella dokumentation godtar CustomTestArgumentEncodable, vissa typer av RawRepresentable, Encodable eller Identifiable med en kodningsbar identifierare. Exemplet uppfyller kodningskravet med posten Codable. En anpassad visningssträng ensam räcker inte. Se Implementing parameterized tests.
Den förväntade aritmetiken för alla fem poster kontrollerades lokalt med heltalsdivision och restberäkning i Python. En illustrativ felaktig implementation som enbart använder heltalsdivision avrundad nedåt fallerar för one-tile och wrap. Swift-filen granskades men kompilerades inte i den här forskningsmiljön eftersom Swift saknades; Xcode-körningen ovan är fortfarande verifieringssteget som faktiskt kör koden.
Bevara det avsedda antalet testfall och livscykeln
En användbar kontroll vid migrering jämför tre saker: antalet scenarioposter, antalet testfall som körs i den fullständiga körningen och om ett valt testfall kan köras ensamt. För det här exemplet är det avsedda antalet fem. Om antalet skiljer sig bör argumentkonstruktionen och testurvalet granskas innan beräkningen ändras.
Håll tillhörande förväntningar tillsammans. Om två separata samlingar skickas till arguments: genereras deras kartesiska produkt. Fem indatavärden och fem förväntade utdata skulle därför ge 25 kombinationer i stället för fem matchade scenarier. Använd separata samlingar när varje kombination är meningsfull; använd poster eller zip() för parade data. Apple förklarar skillnaden i Implementing parameterized tests.
Om du använder zip() ska du kontrollera samlingarnas längd innan du konstruerar paren: Swift stannar vid den kortare sekvensen. Ett extra förväntat resultat skulle annars kunna förbli oanvänt utan att något testfall skapas. En enskild scenariopost undviker just den här sortens avvikelse. Avkortningsbeteendet dokumenteras i Swifts referens för zip(::).
Körordningen påverkar också migreringsbeslutet. Swift Testing kör som standard testfall parallellt. Det här exemplet har oföränderliga argument och ett lokalt testobjekt, så resultaten beror inte på ordningen. Om en parameteriserad funktion tillfälligt behöver köras sekventiellt lägger du till .serialized i dess @Test-attribut. Om det attributet används på en svit körs även dess ingående tester och testfall sekventiellt, men det påverkar inte orelaterade sviter. Se Running tests serially or in parallel.
Sekventiell körning skapar inte nytt tillstånd och definierar inte heller något beroendekontrakt mellan scenarier. Om ett testfall bara lyckas efter att ett annat har ändrat något ska du utforma dess arrangemang på nytt så att det kan köras oberoende. Om stegen avsiktligt ingår i en och samma sammanhängande operation ska du behålla dem i ett test och i stället parameterisera fullständiga operationer.
Använd Swift Testings påståenden genomgående i det här migrerade exemplet. Om du återanvänder hjälpfunktioner som innehåller XCTest-påståenden ska du kontrollera verktygskedjans interoperabilitet: Apples aktuella migreringsguide beskriver lägen och standardvärden som varierar mellan verktygskedjor och versioner av paketverktygen. Genom att hålla filen helt inom Testing undviker du det beroendet. Se Migrating a test from XCTest.
Om migreringen måste vänta är det understödda XCTest-alternativet att skapa en namngiven, parameterlös testmetod per scenario och använda delade beräknings- och kontrollhjälpfunktioner efter behov. Apple rekommenderar ett test per scenario i sin guide för att lägga till tester. Det bevarar separata metodresultat utan att kräva parameterisering med Swift Testing.
Konverteringen är klar när varje ursprungligt scenario har ett motsvarande kört testfall, dess testobjekt startar i avsett tillstånd och ett valt testfall kan köras korrekt på egen hand. Bevara gränsvärdena och de förväntade utdata vid ändringen: separat rapportering är användbar eftersom den gör de befintliga kontrollerna lättare att hitta, diagnostisera och upprepa.
Källor
- 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-projektet
- 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