Swift Testing framework: Kompletný sprievodca novým testovacím systémom a migráciou z XCTest (2026)
Swift Testing je natívny framework od Apple, ktorý nahrádza XCTest. Sprievodca pokrýva @Test, #expect, parametrizáciu, async testy a migráciu krok za krokom.
Swift Testing je nový natívny testovací framework od Apple, ktorý postupne nahrádza XCTest a od Swift 6.0 je súčasťou štandardnej knižnice Swift toolchainu. Namiesto subclassovania XCTestCase používa makrá @Test a #expect, podporuje paralelné spúšťanie testov, parametrizované testy a striktnú integráciu so Swift concurrency. Tento sprievodca ukazuje, ako Swift Testing nasadiť v existujúcom Xcode projekte, prepísať XCTest testy a využiť pokročilé funkcie ako traits, tagy a confirmations. Honestly, prvý raz, keď som ho použil v reálnom projekte (migrácia tristotisíc riadkov produkčného kódu na Swift 6), prekvapilo ma, ako málo som musel meniť.
Swift Testing je natívny framework dostupný od Swift 6.0 a Xcode 16; ku Xcode 26 ho Apple označuje za odporúčaný spôsob testovania nových modulov.
Makro @Test nahrádza metódy začínajúce test, zatiaľ čo #expect a #require nahrádzajú celú rodinu XCTAssert* jedným výrazom.
Parametrizované testy cez arguments: generujú samostatný run pre každý vstup a výrazne redukujú duplicitu test cases oproti for-cyklom v XCTeste.
Suites (@Suite) podporujú init() a deinit namiesto setUp/tearDown, a každý test dostáva čerstvú inštanciu. Žiadne zdieľané mutable state medzi testami.
Swift Testing a XCTest môžu koexistovať v rovnakom test targete, takže migrácia môže prebiehať postupne po jednom súbore.
Traits ako .tags, .bug, .enabled(if:) a .serialized umožňujú metadáta, podmienené spúšťanie a vypnutie paralelizmu bez globálnej konfigurácie.
Čo je Swift Testing a prečo Apple nahrádza XCTest
Swift Testing je open-source testovací framework spravovaný organizáciou swift-lang, ktorý Apple predstavil na WWDC 2024 a v Swift 6.0 prešiel do stabilného API. Na rozdiel od XCTest, ktorý je objektovo orientovaný a viaže sa na Objective-C runtime, je Swift Testing postavený na makrách a generikách priamo nad Swift jazykom. Cieľom je odstrániť historické pasce XCTestu, ako implicitne zdieľaný state cez vlastnosti triedy, kolíziu mien metód, slabú podporu pre async/await a chudobné error messages typu „XCTAssertEqual failed".
Hlavné dôvody, prečo Apple ide týmto smerom: kompilátor vidí test ako bežnú funkciu, takže môže poskytovať diagnostiku s presným zachytením výrazu. Swift Testing pri zlyhaní #expect doslova vypíše „Expectation failed: (a → 3) == (b → 4)", testovacie behy bežia paralelne v rámci jedného procesu a každý test je izolovaný v čerstvej inštancii suite. Ak chcete zároveň využívať aj nový typed throws v Swift 6.2, integrácia s makrom #expect(throws:) špecificky podporuje typovaný error type.
Framework je dostupný na všetkých Apple platformách od iOS 13 a macOS 10.15 vyššie, a tiež na Linuxe a Windowse cez Swift Package Manager. Plnú špecifikáciu nájdete v repozitári swift-testing na GitHube.
Prvý test: @Test, #expect a #require v praxi
Najmenší možný Swift Testing test sa zmestí na tri riadky. V test targete vytvoríte nový Swift súbor, importujete Testing namiesto XCTest a označíte funkciu makrom @Test:
Žiadna trieda, žiadne dedenie, žiadny prefix test. Xcode 16 a novší si funkciu nájde pomocou makra a zobrazí ju v Test Navigatore presne ako XCTest test. Makro #expect nezhodí test pri zlyhaní. Len ho označí ako failed a pokračuje ďalej (analógia XCTAssert). Ak potrebujete tvrdé skončenie testu (typicky pri unwrappingu), použite #require:
#require hodí ExpectationFailedError, čo zastaví ďalšie vyhodnocovanie v rámci testu. Je to ekvivalent XCTUnwrap, ale bez nutnosti meno volania zapamätať. Kompilátor pritom vidí, že po #require je hodnota typu SessionStore.Session, nie optional, takže ďalší kód funguje bez force-unwrappu.
Aký je rozdiel medzi Swift Testing a XCTest?
Rozdiel ide hlbšie ako len nové meno. XCTest je portom OCUnit z Objective-C a zachoval si jeho mentálny model: každý test je metóda triedy, lifecycle riadia setUp/tearDown, asserty sú voľné funkcie s prefixom XCTAssert. Swift Testing je navrhnutý ako Swift-natívny od základov. Testovacie funkcie sú top-level alebo členmi struct/actor, lifecycle používa init/deinit a celá assert vrstva sa zmestila do dvoch makier.
Vlastnosť
Swift Testing
XCTest
Označenie testu
@Test makro
Meno metódy začína test
Assertion API
#expect, #require
~40 funkcií XCTAssert*
Lifecycle
init / deinit per test
setUp/tearDown per trieda
Paralelizmus
Default in-process
Default sekvenčne na hlavnom vlákne
Async/await
Natívna podpora bez wrapperov
Vyžaduje XCTestExpectation alebo async metódy
Parametrizácia
arguments: parameter
For-cyklus alebo manuálne dataset
UI a performance testy
Zatiaľ nepodporuje
Plne podporuje (XCUITest, measure)
Minimum Swift
Swift 6.0
Akýkoľvek Xcode
Praktický dôsledok: nové unit testy by ste mali písať v Swift Testing, ale UI testy a performance benchmarky stále zostávajú v XCTeste. Apple v Xcode 26 release notes potvrdzuje, že integráciu UI testov plánuje až po stabilizácii API. Oficiálna dokumentácia Swift Testing obsahuje aktuálnu matricu podporovaných scenárov.
Ako migrovať z XCTest na Swift Testing krok za krokom
Swift Testing a XCTest môžu žiť v rovnakom test targete bez konfliktu. To znamená, že migráciu môžete robiť postupne, súbor po súbore, bez „big bang" prepisovania. So, ako vyzerá odporúčaný postup pre stredne veľký projekt?
Aktualizujte Package.swift alebo Xcode test target tak, aby cieľoval Swift 6.0+ a importoval Testing. V SPM stačí pridať swiftLanguageMode: .v6.
Vyberte jeden malý XCTest súbor (ideálne pure unit test bez async logiky) ako pilot.
Zmeňte import XCTest na import Testing a odstráňte dedenie z XCTestCase.
Pred každú test metódu pridajte @Test a odstráňte prefix test z mena.
Prepíšte asserty podľa mapovacej tabuľky nižšie.
Spustite testy cez swift test alebo Xcode (CMD+U) a overte, že zelený run prejde.
Iterujte cez ďalšie súbory v batchoch po 3 až 5 týždenne. Nezasekávajte sa na komplexných XCUITest súboroch, tie nechajte tak, ako sú.
Najčastejšie XCTest → Swift Testing mapovania:
// XCTest // Swift Testing
XCTAssertEqual(a, b) → #expect(a == b)
XCTAssertNotEqual(a, b) → #expect(a != b)
XCTAssertTrue(x) → #expect(x)
XCTAssertFalse(x) → #expect(!x)
XCTAssertNil(opt) → #expect(opt == nil)
XCTAssertNotNil(opt) → #expect(opt != nil)
let v = try XCTUnwrap(opt) → let v = try #require(opt)
XCTAssertThrowsError(try f()) → #expect(throws: MyError.self) { try f() }
XCTAssertNoThrow(try f()) → #expect(throws: Never.self) { try f() }
XCTFail("msg") → Issue.record("msg")
Suites, lifecycle a izolácia testov
Test môžete zoskupiť do @Suite, zvyčajne struct, ktorý drží pomocné objekty potrebné pre viacero testov. Najsilnejší rozdiel oproti XCTest? Swift Testing vytvorí novú inštanciu suite pre každý test. Tým pádom mutable property nemôžu pretečiť z jedného testu do druhého a nemusíte písať tearDown na cleanup.
Reťazec spustenia pre každý test je: init() → telo testu → deinit. Ak potrebujete asynchrónny setup (napríklad naliať dáta do SwiftData persistencie), označte init ako async throws. Suite môže byť aj actor; vtedy testy v nej bežia sériovo na actor isolation, čo zjednodušuje testovanie zdieľaného stavu.
Suites môžu byť aj vnorené. Vnorený @Suite dedí display name z rodiča a v Xcode test navigatore sa zobrazí ako hierarchia. To je užitočné napríklad pri pokrytí jedného typu „happy path", „error cases" a „edge cases" oddielmi.
Parametrizované testy a tabuľkové dáta
Parametrizácia bola v XCTest neuralgický bod. Buď ste písali for-cyklus s XCTContext.runActivity, alebo generovali metódy makrom. Swift Testing to rieši natívne cez parameter arguments: v @Test makre:
Test framework vygeneruje samostatný test run pre každú dvojicu. V Test Navigatore uvidíte osem zelených či červených riadkov namiesto jedného. Ak ktorýkoľvek prípad zlyhá, viete presne, ktorý vstup je problematický bez nutnosti dekódovať zlyhanú iteráciu z error message.
Pre cross-product viacerých dimenzií podporuje aj dva arguments polia, ktoré sa pri spustení kartézsky kombinujú:
Tu sa spustí 3 × 3 = 9 testov. Pri parametrizácii vždy zvážte limit, lebo pri stovkách kombinácií dostanete pomalý suite a šum v reportoch. Pre veľké datasety preferujte property-based testing knižnice ako swift-testing-property-based, ktoré samplujú podmnožinu.
Asynchrónne testy a confirmations
Swift Testing rozumie async/await rovnako prirodzene ako Swift sám. Stačí test funkcia označená async throws:
Žiadny XCTestExpectation, žiadny timeout, žiadne waitForExpectations. Pre testy, ktoré overujú, že sa nejaká callback alebo event vystrelí (typicky delegate, Combine subscriber alebo notification), poskytuje Swift Testing makro confirmation:
Po skončení uzáveru framework overí, že confirm() bol zavolaný presne expectedCount-krát. Presne ten use case, kde sa v XCTeste písali komplikované fulfillment(of:timeout:) reťaze. Pri testovaní sieťových klientov sa to dobre kombinuje s URLSession mock vzormi.
Traits, tagy a podmienené spúšťanie
Každý test alebo suite môže prijať traits. Sú to metadáta, ktoré ovplyvňujú beh, alebo len dokumentujú. Najpoužívanejšie:
.enabled(if:) a .disabled(_:), čiže podmienené zapnutie testu (napríklad len na iOS 18+).
.tags(_:), voľné štítky pre filtrovanie z CLI (swift test --filter .tag(slow)).
.bug(_:), odkaz na issue tracker, ktorý sa zobrazí v reporte.
.timeLimit(_:), maximálny čas behu, po prekročení test zlyhá.
.serialized, vypne paralelizmus pre tento test alebo celý suite.
extension Tag {
@Tag static var slow: Self
@Tag static var network: Self
}
@Test(.tags(.slow, .network),
.enabled(if: ProcessInfo.processInfo.environment["CI"] != nil),
.bug("https://github.com/acme/app/issues/4521"))
func endToEndCheckoutFlow() async throws {
// … dlhý e2e test
}
Tagy definujte raz centrálne v jednom súbore a opätovne ich používajte. Pri lokálnom behu rýchlej spätnej väzby spustíte swift test --skip-tag slow a všetky pomalé testy preskočíte. WWDC24 session „Meet Swift Testing" obsahuje celý katalóg dostupných traits.
Paralelné spúšťanie, .serialized a flaky testy
Swift Testing defaultne púšťa testy paralelne v rámci jedného procesu cez Swift concurrency. To znamená dramatické zrýchlenie suite, ale aj okamžitú expozíciu skrytých zdieľaných stavov: singletonov, statických cache, file system kolízii v /tmp. Ak test náhle „začal padať" po migrácii z XCTest, prvý suspect je zvyčajne implicitne zdieľaný stav. (Túto chybu som hľadal pol popoludnia v projekte, kde Core Data stack žil ako globálny singleton.)
Tri stratégie, ako sa s tým vysporiadať:
Refaktor singletonov na injektované dependencies. Najčistejšie, ale najviac práce. Každý test dostane vlastnú inštanciu cez init.
Anotujte suite @Suite(.serialized). Celá skupina pobeží sekvenčne. Vhodné pre Core Data a SwiftData integračné testy, ktoré zdieľajú schému.
Použite .tags a CI matrix. Pomalé testy bežia v oddelenom job-e bez paralelizmu, zvyšok zostáva rýchly.
Ak chcete plné paralelné aj sériové testy v rovnakom suite, nechajte triedu paralelnú a označte len konkrétne metódy @Test(.serialized). Framework garantuje, že serialized testy nebudú bežať súbežne s ostatnými testmi v tej istej suite.
Integrácia so Swift Package Manager a CI
V Swift Package Manager projekte je Swift Testing zapnutý out-of-the-box pre Swift 6.0+. Stačí v Package.swift mať test target a v ňom import Testing:
$ swift test # všetky testy
$ swift test --parallel # explicitne paralelne (default v 6.0+)
$ swift test --filter OrderCalculator # len suite OrderCalculator
$ swift test --skip-tag slow # vynechá tag slow
$ swift test --xunit-output=out.xml # JUnit XML pre CI
Pre GitHub Actions je odporúčaný runner macos-14 alebo novší so Xcode 16+, prípadne ubuntu-24.04 so Swift toolchainom 6.0. JUnit output cez --xunit-output spotrebuje akýkoľvek CI dashboard. Pri integrácii s Xcode Cloud žiadne extra kroky netreba, lebo Apple build images od mája 2026 prichádzajú so Xcode 26 a Swift Testing zapnutým.
Pre veľké monorepá s tisíckami testov sa odporúča rozdeliť spúšťanie podľa tagov do paralelných CI jobov. Typicky unit (rýchle), integration (vyžaduje DB alebo network) a slow (end-to-end). Aktuálne best practices zhrňuje Swift Server guide na swift.org.
Často kladené otázky
Môžem používať Swift Testing a XCTest v jednom projekte súčasne?
Áno. Oba frameworky koexistujú v rovnakom test targete, dokonca v rovnakom súbore. Xcode aj swift test ich spustia spoločne a výsledky agregujú do jedného reportu. Vďaka tomu môžete migrovať postupne bez prerušenia CI.
Je Swift Testing pripravený na produkčné použitie?
Áno, od Swift 6.0 (september 2024) je API stabilné a Apple ho v Xcode 26 release notes označuje za odporúčaný framework pre nové unit testy. UI testy a performance benchmarky zatiaľ zostávajú v XCTeste.
Ako sa testujú async funkcie bez expectations?
Stačí test funkcia označená async throws a priame volanie await. Pre event-based scenáre (delegate, notification) použite makro confirmation, ktoré overí, že callback sa vystrelí presne zadaný počet ráz.
Prečo testy po migrácii padajú náhodne (flaky)?
Najčastejšia príčina je zdieľaný mutable state cez singletony alebo statické cache, ktorý sa pri paralelnom behu prepisuje. Riešením je injektovať dependencies do @Suite struct initu, alebo dočasne anotovať suite .serialized a postupne refaktorovať.
Aký je minimálny Xcode a Swift pre Swift Testing?
Xcode 16 a Swift 6.0 pre integráciu „out of the box". Framework sa dá použiť aj v Xcode 15.3+ ako externá dependencia z balíčka swift-testing, ale Apple od polovice 2025 odporúča prejsť na Xcode 16+ kvôli lepším diagnostikám v test reporte.
Ako pridať haptickú spätnú väzbu v SwiftUI cez SensoryFeedback a Core Haptics. Praktický sprievodca s hodnotami intenzity, AHAP súbormi a príkladmi z produkcie pre iOS 26.
Naučte sa stavať Live Activities a Dynamic Island v iOS 26 cez ActivityKit, App Intents a APNs. Praktické ukážky kódu pre lock screen, StandBy a Smart Stack na watchOS 26.
Kompletný sprievodca prístupnosťou v SwiftUI pre iOS 26. Pokrýva VoiceOver, Dynamic Type, @ScaledMetric, accessibility traits, Reduce Motion, AccessibilityRotor a audio grafy v Swift Charts. S príkladmi kódu a praktickými tipmi z code review.