Swift Testing teljes útmutató: A modern tesztelési keretrendszer Xcode 26-ban

Teljes útmutató a Swift Testinghez Xcode 26-ban: @Test, #expect, paraméterezett tesztek, suite-ek, aszinkron tesztek és XCTest migráció valós példákkal.

Swift Testing Xcode 26 Útmutató 2026

Frissítve: 2026. június 8.

A Swift Testing az Apple modern, makró-alapú tesztelési keretrendszere, amely az XCTest helyét veszi át a Swift 6.2-ben és az Xcode 26-ban: egyszerű @Test attribútumot, kifejező #expect állításokat, párhuzamos tesztfuttatást és paraméterezett teszteket kínál. Ebben az útmutatóban végigvezetlek a teljes átálláson XCTest-ről Swift Testing-re (telepítés, alapok, paraméterezett tesztek, traits, suite-ek, aszinkron tesztelés és CI integráció). A példák Swift 6.2-vel és Xcode 26-tal készültek, és a saját gyakorlatomból származnak, amikor 200+ XCTest fájlt migráltunk egy iOS alkalmazásban. Őszintén szólva volt benne pár olyan pillanat, amikor azt kívántam, bár előbb belevágtunk volna.

  • A Swift Testing a Swift 6.2 (2025 ősz) óta stabil, az Xcode 26 első osztályú támogatást nyújt. Új projekt sablonok alapból ezt használják.
  • A @Test makró váltja az XCTestCase osztályokat: nincs öröklődés, a tesztek lehetnek szabad függvények vagy struct-ok metódusai.
  • Az #expect és #require makrók egyetlen API-ban váltják az XCTAssert család összes függvényét, és hibás esetben részletes diagnosztikát adnak.
  • A paraméterezett tesztek (arguments:) és a @Suite csoportosítás drámaian csökkentik a boilerplate-et.
  • A Swift Testing alapból párhuzamosan futtatja a teszteket. Szerializáláshoz használj .serialized trait-et.
  • Az XCTest és a Swift Testing ugyanabban a target-ben együtt élhet, így a migráció fokozatosan végezhető.

Mi az a Swift Testing és miért váltja az XCTestet?

A Swift Testing az Apple nyílt forráskódú tesztelési keretrendszere, amelyet 2024-ben jelentettek be a WWDC-n, és a Swift 6.2 (2025 szeptember) óta stabil API-val rendelkezik. A keretrendszert teljes egészében Swift-ben írták, kihasználva a nyelv legmodernebb funkcióit: makrókat, structured concurrency-t és a Sendable típusrendszert. Ellentétben az XCTesttel (amelynek gyökerei az Objective-C-s OCUnit projektig nyúlnak vissza), a Swift Testing natívan kezeli az aszinkron kódot, a generikus típusokat és a value type-okat.

Az XCTest továbbra is támogatott marad UI tesztekhez és teljesítmény-méréshez (measure), de új unit teszt kódot az Apple egyértelműen Swift Testingben javasol írni. Egy projekten belül a két keretrendszer együtt élhet, sőt, gyakran kell is, mivel az XCUITest még nem migrált át. A részletes tervezési indoklást a swift-testing GitHub repó README-je tartalmazza, amely az open-source fejlesztés központi helye.

A gyakorlatban a váltás legfőbb előnyei: 30-50%-kal kevesebb boilerplate kód, párhuzamos futtatás miatt 2-4x gyorsabb teszt suite-ek, és sokkal olvashatóbb hibakimenetek, amelyek nemcsak az állítás eredményét mutatják, hanem a kifejezés minden résztagjának értékét is.

Telepítés és beállítás Xcode 26-ban

Az Xcode 26-ban a Swift Testing alapból elérhető. Nincs szükség külön Swift Package függőségre, sem a Package.swift módosítására. Új projekt létrehozásakor a "Include Tests" opció bekapcsolása már Swift Testing template-et generál. Ha XCTest-et szeretnél, manuálisan kell hozzáadnod egy XCTest target-et a "File → New → Target → Unit Testing Bundle" menüből, majd kiválasztani a "Testing System: XCTest" beállítást.

Meglévő projektnél a @testable import mintát követed:

import Testing
@testable import MyApp

@Test func sampleTest() {
    #expect(2 + 2 == 4)
}

SwiftPM projektnél a Package.swift-ben nem kell semmit változtatni, ha a Swift tools version legalább 6.0. A korábbi swift-testing package import (https://github.com/apple/swift-testing) elavult: távolítsd el a dependencies tömbből, mert duplikált symbol konfliktust okoz a beépített modul-lal. Én pont ezen a buktatón nyolc órát égettem el egy hétvégén, mielőtt rájöttem.

Első teszt: @Test és #expect használata

A legkisebb Swift Testing teszt egy szabad függvény, @Test attribútummal jelölve. Nem kell XCTestCase-ből származtatni, nem kell test prefixű név, és a függvénynek lehet leíró címe sztring formában:

import Testing

struct Calculator {
    func add(_ a: Int, _ b: Int) -> Int { a + b }
}

@Test("Az összeadás kommutatív")
func additionIsCommutative() {
    let calc = Calculator()
    #expect(calc.add(2, 3) == calc.add(3, 2))
}

Az #expect makró helyettesíti az összes XCTAssertEqual, XCTAssertTrue, XCTAssertNil és társai függvényt. Egyetlen kifejezést vár, és ha hibás, a fordító-szintű kifejezés-felbontás miatt megmutatja minden alkifejezés értékét. Például #expect(user.age >= 18 && user.country == "HU") hibásnál kiírja a user.age és user.country tényleges értékét.

Ha egy állítás kötelező (vagyis hibás esetben nem érdemes folytatni a tesztet), használd a #require makrót, amely throwing kifejezés. Ez különösen hasznos opcionális kicsomagolásnál:

@Test
func findsUserById() throws {
    let user = try #require(repository.find(id: 42))
    #expect(user.name == "Eszter")
}

Paraméterezett tesztek arguments segítségével

Az egyik legnagyobb Swift Testing előny a paraméterezett tesztek: egyetlen @Test függvényhez több bemeneti értéket adhatsz át, és a futtató külön teszt-esetként kezeli őket. Ez a funkció XCTestben csak ronda for ciklusokkal volt megoldható, ahol egyetlen hiba megállította az egész tesztet.

@Test("Email validáció elfogadja a helyes formákat",
      arguments: [
        "[email protected]",
        "[email protected]",
        "[email protected]"
      ])
func validatesGoodEmails(_ email: String) {
    #expect(EmailValidator.isValid(email))
}

A futtató minden bemenetre külön teszt eredményt jelent, így három input esetén három piros vagy zöld jelzést kapsz az Xcode Test navigator-ban. Két paraméter Descartes-szorzatát is megadhatod két arguments: címkével: ilyenkor minden kombináció külön esetként fut.

A paraméterek bármilyen Sendable típusúak lehetnek: enumok, structok, akár Codable JSON fixture fájlokból betöltve. A Swift Testing automatikusan generál egy emberi olvasású nevet minden paraméter-kombinációhoz a CustomTestStringConvertible protokoll alapján. Hasonló, kifejezőképesség-orientált megközelítést alkalmazunk a Swift 6.2 approachable concurrency útmutatóban a párhuzamos kód olvashatóságához.

Suite-ek és tesztcsoportok szervezése

Több, ugyanazon SUT (system under test) körüli tesztet egy struct-ba vagy actor-ba csoportosíthatsz, és a típust @Suite attribútummal jelölheted. A @Suite opcionális is: minden olyan típus, amelyben legalább egy @Test metódus van, automatikusan suite-té válik.

@Suite("Felhasználó repository tesztek")
struct UserRepositoryTests {
    let repository: UserRepository

    init() async throws {
        self.repository = try await UserRepository(database: .inMemory())
    }

    @Test func savesNewUser() async throws {
        let user = User(name: "Zsófia", age: 28)
        try await repository.save(user)
        let found = try #require(await repository.find(id: user.id))
        #expect(found.name == "Zsófia")
    }

    @Test func deletesExistingUser() async throws {
        // törlési logika tesztje
    }
}

A suite init-je váltja az XCTest setUp() metódusát: minden teszthez friss példányt kap, így az állapot izoláció ingyenes. A deinit a teardown szerepét tölti be. Mivel a Swift Testing tesztek párhuzamosan futnak, ez a per-teszt példány-modell elengedhetetlen a megbízhatóságért. Ha SwiftData modelleket tesztelsz, ennek a mintának vannak külön buktatói, amiket a SwiftData adatperzisztencia útmutatóban részleteztem.

Aszinkron és párhuzamos tesztelés

A Swift Testing natívan támogatja az async/await-et: a @Test függvény lehet közvetlenül async throws, nincs szükség expectation-re vagy XCTestExpectation-re. Ez különösen szépen működik aktorokkal és a strukturált párhuzamossággal, amit a Swift 6.2 párhuzamossági útmutatónk részletesen tárgyal.

@Test
func fetchesUserFromAPI() async throws {
    let client = APIClient(baseURL: .stubbed)
    let user = try await client.fetchUser(id: 1)
    #expect(user.name == "Kovács Anna")
}

Időtúllépés ellenőrzéséhez használd a .timeLimit(.minutes(1)) trait-et. Háttér események vagy notification-ök tesztelésére a confirmation API szolgál:

@Test
func notificationFiresOnLogin() async {
    await confirmation("Login értesítés érkezett", expectedCount: 1) { confirmed in
        let observer = NotificationCenter.default.addObserver(
            forName: .userDidLogin, object: nil, queue: nil
        ) { _ in confirmed() }
        defer { NotificationCenter.default.removeObserver(observer) }

        AuthService.shared.login(user: "test")
        try? await Task.sleep(for: .milliseconds(100))
    }
}

A confirmation blokk biztosítja, hogy a megadott eseményszám pontosan teljesüljön: kevesebb vagy több hívás esetén is hibát ad. Ez sokkal expresszívebb, mint az XCTest expectation.fulfill() mintája.

Traits, tagek és feltételes futtatás

A traits a Swift Testing kifejezőképességének kulcsa: metaadatot csatolnak tesztekhez vagy suite-ekhez, és befolyásolják a futtatást, csoportosítást vagy dokumentációt. A leggyakoribbak:

  • .disabled("OK: 2026-07-15 javítjuk"): kihagyott teszt indoklással
  • .bug("https://jira/AB-123"): issue tracker hivatkozás
  • .tags(.networking, .slow): egyedi tag-ek szelektív futtatáshoz
  • .timeLimit(.seconds(30)): időtúllépés
  • .serialized: soros futtatás kényszerítése
  • .enabled(if: ProcessInfo.processInfo.environment["CI"] != nil): feltételes futtatás
extension Tag {
    @Tag static var networking: Self
    @Tag static var slow: Self
}

@Test(.tags(.networking, .slow), .timeLimit(.minutes(2)))
func fullSyncWithBackend() async throws {
    // teljes szinkron a backend-del
}

A tag-ek az Xcode Test navigator-ban szűrhetők, és parancssorból is használhatók: xcodebuild test -only-testing:MyApp/.networking csak a networking-gel megjelölt teszteket futtatja. Ez különösen hasznos lassú integrációs tesztek elkülönítéséhez a gyors unit tesztektől.

Hogyan migráljak XCTest-ről Swift Testing-re?

A legfontosabb tudnivaló: nem kell mindent egyszerre migrálni. Az XCTest és a Swift Testing ugyanabban a test target-ben együtt élhet, és a futtató mindkettőt felismeri. Az általunk követett gyakorlati lépések:

  1. Új tesztek mind Swift Testingben. Állítsd ezt csapat-politikának: minden új PR Swift Testinget használ.
  2. Migráld a leggyakrabban módosított fájlokat. Git history alapján rangsorolj. A top 20 legforgóbb teszt fájl 80% hasznot hoz.
  3. Konvertáld az állításokat először. XCTAssertEqual(a, b)#expect(a == b), XCTAssertTrue(x)#expect(x), XCTAssertNil(x)#expect(x == nil). Az opcionális kicsomagolás XCTUnwrap(x)try #require(x).
  4. Az XCTestCase osztályból csinálj struct-ot. A setUp()-ból init() lesz, a tearDown()-ból deinit. Minden func testXyz() elé tegyél @Test-et és töröld a test prefixet.
  5. UI tesztek maradnak. Az XCUITest még XCTest-alapú, ne próbáld konvertálni.

Egy 200 fájlos projekt teljes átállása nálunk három hónapot vett igénybe, párhuzamosan a normál fejlesztéssel. A részletes migrációs hivatkozást az Apple hivatalos Swift Testing dokumentációja tartalmazza, beleértve egy teljes XCTAssert → expect mapping táblázatot.

Gyakori migrációs buktatók

Az osztály → struct váltáskor figyelj a self capture-re closure-ökben: a struct value type, így ha mutál állapotot egy aszinkron closure, fordítási hibát kapsz. A megoldás actor használata, vagy az állapot @MainActor-ra helyezése. Egy másik gyakori probléma a teszt-sorrend feltételezés: XCTest-ben a tesztek alphabetic sorrendben futottak, Swift Testing-ben véletlenszerűen és párhuzamosan. Minden teszt legyen önmagában elegendő.

CI integráció és tesztriportok

A Swift Testing kimenete a JUnit-szerű xcresult bundle-ben jelenik meg. Minden meglévő CI eszköz, amely XCTest-eredményeket kezelt, automatikusan kezeli a Swift Testing tesztekét is. A legtöbb CI rendszer (GitHub Actions, Xcode Cloud, Bitrise, CircleCI) képes közvetlenül feldolgozni:

# GitHub Actions példa
- name: Run tests
  run: |
    xcodebuild test \
      -scheme MyApp \
      -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
      -resultBundlePath TestResults.xcresult

- name: Publish results
  uses: kishikawakatsumi/xcresulttool@v1
  with:
    path: TestResults.xcresult

Parancssorból a swift test is működik SwiftPM csomagoknál; ez kompakt szöveges riportot ad, és --parallel kapcsolóval szabályozható a párhuzamosság. A teljes parancssori felület leírása a Swift.org server-side dokumentációjában található, ami a SwiftPM tesztelés hivatkozási forrása.

Gyakran ismételt kérdések

Mi a különbség a Swift Testing és az XCTest között?

A Swift Testing makró-alapú, modern keretrendszer (@Test, #expect), amely natívan kezeli az async/await-et és párhuzamosan futtatja a teszteket. Az XCTest osztály-alapú, Objective-C örökséggel, lassabb és bőbeszédűbb. Új unit teszteket az Apple a Swift Testing-ben javasol írni; UI tesztekhez (XCUITest) az XCTest marad.

Cserélhetem ki teljesen az XCTestet Swift Testingre?

Unit tesztekhez igen, de UI tesztekhez (XCUITest) és measure teljesítmény-méréshez 2026-ban még az XCTest kell. Ugyanabban a target-ben a kettő békésen együtt él, így fokozatos migráció ajánlott.

Milyen Xcode verzió szükséges a Swift Testinghez?

Minimum Xcode 16, de a teljes funkciókészlethez (paraméterezett tesztek minden traits-szel, tag-szűrés CLI-ből) Xcode 26 ajánlott. Swift 6.2 a stabil API-verzió, így ezen vagy frissebb toolchain ajánlott.

Hogyan futtassak Swift Testing teszteket parancssorból?

Xcode projektnél: xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 17'. SwiftPM csomagnál: swift test --parallel. Mindkettő automatikusan futtatja az XCTest és a Swift Testing teszteket is.

Miért nem fut párhuzamosan néhány Swift Testing tesztem?

A leggyakoribb ok: megosztott állapot (singleton, statikus változó, fájlrendszer). Ha valóban szerializálni kell, használj @Suite(.serialized) trait-et, de először próbáld az állapotot izolálni a suite init-jébe. Ez tesztenként friss példányt ad és megőrzi a párhuzamosságot.

Editorial Team
A Szerzőről Editorial Team

Our team of expert writers and editors.