Swift Testing on Applen uusi natiivi testauskehys Swiftille, joka korvaa vanhan XCTestin selkeämmillä makroilla, kuten @Test, #expect ja @Suite. Se tukee parametrisoituja testejä, taggausta, rinnakkaista suoritusta ja täysimittaista async/await-syntaksia ilman erillisiä odotusobjekteja. Xcode 26 ajaa Swift Testing- ja XCTest-testejä rinnakkain samassa kohteessa, joten siirtymä voi tapahtua asteittain. Tämä opas käy läpi käyttöönoton, makrot, migraatiopolun ja yleisimmät sudenkuopat tuotantoprojektissa.
Swift Testing toimitetaan Swift 6.0+ -työkaluketjun mukana ja se on tuettu natiivisti Xcode 16:sta alkaen. Vuoden 2026 Xcode 26 lisää parannetut diagnostiikkanäkymät ja Issue Navigator -integraation.
@Test-makro korvaa func test...()-nimeämiskonvention ja sallii kuvaavat ihmisluettavat testinimet sekä metadatan, kuten tagit ja aikarajat.
#expect ja #require korvaavat lähes 40 erillistä XCTAssert-funktiota yhdellä lausekepohjaisella API:lla, joka näyttää epäonnistumisen yhteydessä koko lausekkeen ja sen osa-arvot.
Parametrisoidut testit arguments:-parametrin kautta vähentävät boilerplatea ja ajavat jokaisen tapauksen omana testinään rinnakkain.
Swift Testing ei korvaa XCTestiä XCUITest- tai suorituskykytesteissä. Jätä nämä yhä XCTestiin ja siirrä vain yksikkötestit.
Asteittainen siirtymä on käytännössä turvallisin tie: pidä molemmat kehykset samassa testikohteessa ja konvertoi tiedostoja sitä mukaa kun ne muuttuvat.
Mikä on Swift Testing -kehys?
Swift Testing on avoimen lähdekoodin testauskehys, jonka Apple julkaisi WWDC 2024:ssä ja joka on osa Swift-työkaluketjua versiosta 6.0 alkaen. Se on suunniteltu hyödyntämään Swiftin moderneja kielipiirteitä (makroja, rakenteellista samanaikaisuutta, generikkoja ja tulostyyppiä) sen sijaan, että nojaisi Objective-C:n NSInvocation-periaatteisiin kuten XCTest. Kehyksen tavoite on yksinkertainen: tehdä testien kirjoittamisesta ilmaisuvoimaisempaa, lukea epäonnistumisia paremmin ja ajaa testejä turvallisesti rinnakkain.
Konkreettisesti tämä tarkoittaa, että testifunktioita ei enää tarvitse nimetä testJotain()-konvention mukaisesti, vaan testit merkitään @Test-makrolla. Assertiot kirjoitetaan natiiveilla Swift-lausekkeilla, kuten #expect(user.age >= 18), ja kehys osaa hajottaa epäonnistuneen lausekkeen ja näyttää, mitä kumpikin puoli sisälsi virheen hetkellä. Apple jatkaa XCTestin ylläpitämistä yhteensopivuuden vuoksi, mutta uudet ominaisuudet (kuten parantunut rinnakkaisuus ja deterministinen suoritusjärjestys) kohdistetaan pelkästään Swift Testingiin.
Kehys on saatavilla myös Linuxilla ja Windowsilla saman Swift-paketin kautta, mikä tekee siitä järkevän valinnan monialustaiseen Swift-koodiin. Lähdekoodi ja täydellinen dokumentaatio löytyvät swift-testing-repositoriosta GitHubissa.
Käyttöönotto Xcode 26:ssa
Xcode 26 sisältää Swift Testingin valmiina. Erillistä paketin lisäämistä ei tarvita iOS-, macOS-, watchOS- tai visionOS-projekteissa. Uusissa projekteissa Xcode luo automaattisesti Swift Testing -pohjaisen testitiedoston, kun valitset "Include Tests" -valintaruudun. Olemassa oleviin projekteihin voit lisätä uuden testitiedoston ja kirjoittaa Swift Testing -koodin samaan testikohteeseen, jossa XCTest-tiedostot jo ovat: kumpikin kehys löytää testinsä itsenäisesti.
Swift Package Manager -projekteissa käyttöönotto vaatii pakettimanifestin swift-tools-version-version asettamisen vähintään arvoon 6.0. Tämän jälkeen Swift Testing on käytettävissä ilman uusia riippuvuuksia.
Testitiedostossa importtaa Testing-moduuli, älä XCTest-moduulia, ellet aio sekoittaa kehyksiä samassa tiedostossa.
import Testing
@testable import MyLibrary
@Test func laskeeSummanOikein() {
let summa = Laskin.summa(2, 3)
#expect(summa == 5)
}
@Test-makro ja @Suite-organisointi
Swift Testing korvaa XCTestin perintöpohjaisen mallin makropohjaisella mallilla. @Test-makro merkitsee yksittäisen funktion testiksi riippumatta siitä, sijaitseeko se vapaassa scopessa vai struct/class/actor-tyypin sisällä. Funktion nimellä ei ole semanttista merkitystä, mutta makron parametri sallii ihmisluettavan otsikon, joka näkyy Xcoden Test Navigatorissa.
@Suite-makro ryhmittelee toisiinsa liittyvät testit yhteen tyyppiin. Sviitti on tyypillisesti struct, joka luodaan uudelleen ennen jokaista testiä. Tämä tarkoittaa, että voit asettaa alkutilan tavallisessa initializerissa ilman erillistä setUp()-metodia. Mielestäni tämä on yksi kehyksen suurimmista ergonomian parannuksista verrattuna XCTestiin.
Sviitteihin voi myös liittää traitseja: .tags(.network) testien suodatusta varten, .timeLimit(.minutes(1)) aikarajan asettamiseen tai .disabled("Odottaa korjausta #1234") testin tilapäiseen poiskytkemiseen. Traitit kannattaa määritellä omassa laajennoksessaan, jotta ne ovat yksilöllisesti löydettävissä ja uudelleenkäytettävissä projektissa.
#expect ja #require: uusi assertio-API
XCTestissä kehittäjän piti muistaa, kumpaa kymmenistä XCTAssertEqual-, XCTAssertGreaterThan- tai XCTAssertNotNil-funktioista käyttää. Swift Testing pelkistää tämän kahdeksi makroksi: #expect kirjaa epäonnistumisen mutta jatkaa testin ajoa, ja #require heittää poikkeuksen ja keskeyttää testin, jos ehto ei toteudu.
Tärkein etu on diagnostiikka. Kun #expect(kori.kokonaishinta == 12.40) epäonnistuu, virhetuloste näyttää lausekkeen molemmat puolet: kori.kokonaishinta → 12.0 ja 12.40. XCTestissä olisit nähnyt vain "XCTAssertEqual failed: ("12.0") is not equal to ("12.40")". Tämä toimii myös kompleksisemmissa lausekkeissa, kuten #expect(taulukko.contains { $0.id == kohdeId }).
Voit lisätä epäonnistumiselle kommentin lainausmerkkiparametrina:
#expect(token != nil, "Tokenin pitäisi olla tallennettu keychainiin onnistuneen sisäänkirjautumisen jälkeen")
Parametrisoidut testit
Parametrisoidut testit ovat alue, jossa Swift Testing erottuu selvimmin XCTestistä. Yksi @Test-funktio voi ajaa kymmeniä tai satoja tapauksia arguments:-parametrin kautta, ja jokainen tapaus näkyy omana rivinä Test Navigatorissa.
Honestly, tämä on ominaisuus, jota kaipasin pitkään XCTestissä. Aiemmin yleinen for-silmukka pysähtyi heti ensimmäiseen epäonnistumiseen, ja loput tapaukset jäivät pimentoon. Nyt kaikki tapaukset ajetaan, kaikki epäonnistumiset raportoidaan, ja voit ajaa uudelleen vain epäonnistuneet tapaukset Xcoden käyttöliittymästä.
Async/await ja samanaikaisuus testeissä
Swift Testing tukee async- ja throws-funktioita natiivisti, joten testit voivat olla suoraan asynkronisia ilman XCTestExpectation-objektien luomista ja wait(for:timeout:)-kutsuja. Tämä on erityisen arvokasta projekteissa, jotka käyttävät runsaasti Swift Concurrencyä, kuten artikkelimme Swift 6.2:n lähestyttävästä rinnakkaisuudesta kuvailee.
Jos sinun täytyy odottaa tiettyä tapahtumaa, esimerkiksi delegaattimetodin kutsumista tai NotificationCenter-ilmoitusta, käytä confirmation-API:a. Se odottaa nimettyä vahvistusta ja epäonnistuu, jos sitä ei kutsuta odotetussa aikarajassa.
Rinnakkaisuuden hallintaan kehys tarjoaa .serialized-traitin. Käytä sitä sviiteissä, jotka mutatoivat globaaleja resursseja tai jotka kommunikoivat saman tietokannan kanssa. Muuten ajojärjestys on epädeterministinen ja testit voivat häiritä toisiaan.
Miten siirryn XCTestistä Swift Testingiin?
Asteittainen siirtymä on käytännössä turvallisin tapa. Xcode ajaa Swift Testing- ja XCTest-testit rinnakkain samassa testikohteessa, joten et joudu konvertoimaan koko testisuiteta kerralla. Itse törmäsin tähän viimeisimmässä asiakasprojektissani: yritin ensin migratoida kaiken kerralla, ja CI rikkoutui tuntikausiksi. Toiseen kierrokseen otin tiedosto kerrallaan -strategian, joka toimi paljon paremmin. Tässä toimiva järjestys:
Päivitä Xcode ja Swift-versio. Varmista, että projektin minimi Swift-versio on 6.0 tai uudempi. Tämä ei vaikuta tuotantokoodiin, jos olet jo ottanut Swift 6:n käyttöön. Katso oppaamme Swift 6.2:n samanaikaisuudesta.
Konvertoi yksinkertaisimmat tiedostot ensin. Aloita tiedostoista, joissa on vain perustason XCTAssertEqual- ja XCTAssertTrue-kutsuja ilman setUp/tearDown-logiikkaa.
Korvaa setUp/tearDown initializerillä ja deinitializerillä. Konvertoi XCTestCase-luokat @Suite-rakenteiksi. Siirrä setUp()-koodi init()iin ja tearDown()-koodi deinitiin.
Korvaa XCTestExpectation async/awaitilla. Useimmat odotuspohjaiset testit voidaan kirjoittaa uudelleen suoraan async-funktioiksi.
Säilytä UI- ja suorituskykytestit XCTestissä. XCUITest ja measure { }-API:t ovat edelleen vain XCTestissä, älä yritä siirtää näitä.
Useimmat XCTAssert-funktiot kartoittuvat suoraan:
XCTest
Swift Testing
XCTAssertEqual(a, b)
#expect(a == b)
XCTAssertTrue(x)
#expect(x)
XCTAssertNil(x)
#expect(x == nil)
XCTUnwrap(x)
try #require(x)
XCTAssertThrowsError
#expect(throws: VirheTyyppi.self) { ... }
XCTFail("...")
Issue.record("...")
Onko Swift Testing parempi kuin XCTest?
Useimpiin yksikkötestaustarpeisiin kyllä, mutta XCTest säilyttää joitain ainutlaatuisia rooleja. Swift Testing voittaa ilmaisuvoimassa, diagnostiikan tarkkuudessa, parametrisoinnissa ja rinnakkaisajossa. Sen makropohjainen API tuottaa luettavampia testitiedostoja, ja struct-pohjaiset sviitit eliminoivat luokkaperinnön epäselvyydet täysin. Apple keskittää myös uudet investoinnit kehykseen: deterministiset testijärjestykset, parannettu Xcode-integraatio ja Linux/Windows-tuki kehittyvät vain Swift Testingissä.
XCTest pysyy kuitenkin tarpeellisena kahteen tarkoitukseen. Ensinnäkin UI-testit XCUITestin kautta, koska Swift Testing ei tarjoa korviketta XCUIApplication- ja XCUIElement-luokille. Toiseksi suorituskykytestit measure { }-API:lla, jota Swift Testing ei vielä tue suoraan. Käytännössä useimpien tiimien lopullinen rakenne on hybridi: yksikkötestit Swift Testingillä, UI- ja suorituskykytestit XCTestillä, kaikki samassa testikohteessa.
Swift Testing on myös avoimen lähdekoodin alusta, jota voidaan käyttää palvelinpuolen Swift-projekteissa ja Linux/Windows-CI:ssä. Apple julkaisi yksityiskohtaisen vertailun virallisessa Swift Testing -dokumentaatiossa, joka kannattaa lukea ennen suuria siirtymäpäätöksiä.
Yleiset sudenkuopat ja parhaat käytännöt
Suurin yllätys XCTestistä tuleville on rinnakkaisuuden oletusarvo. Testit ajetaan samanaikaisesti useilla säikeillä, mikä paljastaa kaikki piilevät jaetun tilan virheet. Jos kaksi testiä kirjoittaa samaan tiedostoon, mutatoi samaa singletonia tai konfiguroi globaaleja URLProtocol-rekistereitä, näet sattumanvaraisia epäonnistumisia. Itse törmäsin tähän bugin shippauksessa: yhden URLProtocol-rekisterin testit kaatuivat satunnaisesti, ja syyn löytäminen vei pari iltapäivää. Korjaus on lähes aina .serialized-traitin lisääminen kyseiseen sviittiin tai testikoodin refaktoroiminen niin, että jaettu tila eliminoidaan.
Toinen yleinen sudenkuoppa on @MainActor-eristyksen unohtaminen testeissä, jotka koskevat SwiftUI-näkymiä tai UIKit-kontrollereita. Swift Testing kunnioittaa kielen samanaikaisuussääntöjä, joten käyttöliittymäkomponenttien testit tarvitsevat eksplisiittisen @MainActor-annotaation funktion tai sviitin tasolla. Tämä yhdistyy hyvin @Observable-makron käyttöön, jossa pääsääntö on samanlainen.
Kolmas asia, jonka tiimit huomaavat liian myöhään, on testitiedostojen koodin uudelleenkäyttö. Koska sviitit ovat structeja, joiden initializeri ajetaan jokaisen testin kohdalla uudelleen, kalliit operaatiot (verkkomockit, tietokannan rakentaminen, suuret testidatat) kannattaa rakentaa staattisina ominaisuuksina tai jaettuna actorina, ei initializerissä. Tämä pitää testikohteen nopeana, vaikka testimäärä kasvaa.
Usein kysytyt kysymykset
Voiko Swift Testing- ja XCTest-testejä ajaa samassa projektissa?
Kyllä. Xcode 26 löytää molemmista kehyksistä testit automaattisesti samasta testikohteesta. Tämä on suositeltu tapa siirtyä asteittain: säilytä XCUITest- ja suorituskykytestit XCTestissä ja konvertoi yksikkötestit Swift Testingiin omalla tahdillaan.
Tukeeko Swift Testing iOS 15:ttä tai sitä vanhempia versioita?
Kyllä. Swift Testing kohdistuu testikohteeseen, ei tuotantokoodiin. Vaikka sovelluksesi tukisi iOS 15:tä, voit silti kirjoittaa testit Swift Testingillä, kunhan Xcode-versio on 16 tai uudempi.
Mikä on ero #expect- ja #require-makrojen välillä?
#expect kirjaa epäonnistumisen mutta jatkaa testin suoritusta, jotta näet kaikki epäonnistumiset kerralla. #require heittää poikkeuksen ja pysäyttää testin, joten käytä sitä vain silloin, kun testin loput rivit eivät voi järkevästi edetä ilman ehdon toteutumista.
Miten kirjoitan parametrisoidun testin Swift Testingissä?
Käytä @Test(arguments: [...])-syntaksia ja lisää funktiolle parametri, joka vastaa jokaista argumenttilistan alkiota. Kehys ajaa testin kerran jokaista argumenttia kohti ja näyttää jokaisen tapauksen omana rivinä Test Navigatorissa.
Voinko käyttää Swift Testingiä komentoriviltä CI-ympäristössä?
Kyllä. Komento swift test ajaa molemmat Swift Testing- ja XCTest-testit Swift Package Manager -projekteissa. Xcode-projekteissa käytä xcodebuild test normaalisti, koska se tunnistaa molemmat kehykset automaattisesti.
Opi rakentamaan Live Activities -toimintoja ja Dynamic Island -käyttöliittymiä iOS 26:lle ActivityKitin ja SwiftUI:n avulla. Sisältää APNs-integraation, koodiesimerkit ja parhaat käytännöt vuodelle 2026.
Swift-makrot poistavat toistuvan boilerplate-koodin ja siirtävät virheitä käännösaikaan. Opi luomaan omia makroja SwiftSyntax-kirjastolla — URL-validoinnista CodingKeys-generointiin. Mukana Swift 6.2:n tuomat parannukset.