XCTest: kernbegrippen, XCTestCase-klassen en het schrijven van tests

Auteur: IT Sectr Gepubliceerd: 2026-04-08 Leestijd: 11 min

XCTest — is het Apple-framework voor modulair en integratietesten van applicaties voor iOS, macOS, watchOS en tvOS. XCTest maakt deel uit van Xcode en ondersteunt het schrijven van tests in Swift en Objective-C. In tegenstelling tot externe frameworks (Quick, Nimble) is XCTest de officiële oplossing van Apple en volledig geïntegreerd met Xcode Server en CI/CD. Volgens Apple Developer (2024) wordt XCTest gebruikt in 94% van de iOS-apps uit de top 100 van de App Store. XCTest biedt een stabiele basis voor het schrijven van unit-tests en UI-tests zonder externe afhankelijkheden.

Belangrijkste punten

  • XCTest — het officiële Apple-framework voor unit-testen en UI-testen in Swift en Objective-C.
  • XCTestCase — de basisklasse voor alle tests die setUp, tearDown en assert-methoden biedt.
  • Asserts — XCTAssertTrue, XCTAssertEqual, XCTAssertNil en andere om verwachte resultaten te controleren.
  • XCTestExpectation — mechanisme voor het testen van asynchrone code met verwachting van uitvoering.
  • Performantietests — meting van uitvoeringstijd van code via de methode measure(metrics:) met drempels.

Wat is XCTest?

XCTest — is een framework voor modulair, integratie- en UI-testen, ontwikkeld door Apple en ingebouwd in Xcode sinds versie 5.0 (2013). XCTest verving OCUnit (SenTestingKit) en bood een moderne API in Swift met ondersteuning voor asynchrone tests, performantietests en integratie met Xcode Server. Volgens Swift.org (2024) is XCTest de basis voor testen in alle Apple-projecten, waaronder Swift Package Manager, die XCTest gebruikt voor zelfcontrole.

XCTest werkt samen met Xcode Test Navigator en Report Navigator, die de testboom, uitvoeringsgeschiedenis tonen en resultaten vergelijken tussen builds. Test Navigator maakt het mogelijk om één test, een groep tests of de volledige set uit te voeren zonder code te wijzigen. Resultaten worden weergegeven als groene (passed), rode (failed) en gele (skipped) pictogrammen. Volgens Apple WWDC (2024) heeft Xcode 16 het parallel uitvoeren van tests met 40% verbeterd door het gebruik van meerdere simulatoren.

XCTest ondersteunt platforms: iOS 8.0+, macOS 10.10+, watchOS 2.0+, tvOS 9.0+. Voor elk platform is dezelfde API beschikbaar, wat het schrijven van cross-platform tests mogelijk maakt. Swift Testing — een nieuw Apple-framework (aangekondigd in 2024) dat in de toekomst XCTest zal aanvullen, maar het niet volledig zal vervangen. XCTest blijft het belangrijkste framework voor testen in het Apple-ecosysteem.

XCTestCase — de basisklasse voor tests

XCTestCase — is de basisklasse waarvan alle testklassen in XCTest overerven. Het biedt de levenscyclus van de test: `setUp()` wordt aangeroepen voor elke test, `tearDown()` — na elke test. SetUp wordt gebruikt voor initialisatie van objecten en mocks, tearDown — voor het opschonen van resources. setUpWithError en tearDownWithError maken het mogelijk om initialisatiefouten af te handelen zonder try-catch in elke test.

Elke methode waarvan de naam begint met `test` wordt automatisch door Xcode herkend als test. Alternatief kan het macro `@Test` (Swift Testing) worden gebruikt. De testnaam moet beschrijvend zijn: `testLoginWithValidCredentials` is beter dan `testLogin1`. Documentatie van tests via commentaren is een goede gewoonte, maar Xcode biedt de mogelijkheid om beschrijvingen toe te voegen via User-Defined Attributes.

swift
import XCTest

class UserServiceTests: XCTestCase {

    var sut: UserService!
    var mockSession: MockURLSession!

    override func setUp() {
        mockSession = MockURLSession()
        sut = UserService(session: mockSession)
    }

    override func tearDown() {
        sut = nil
        mockSession = nil
    }

    func testFetchUser_ReturnsDecodedUser() {
        let json = "{\"id\": 1, \"name\": \"Alice\"}"
        mockSession.setResponse(json)
        let user = try await sut.fetchUser(id: 1)
        XCTAssertEqual(user.name, "Alice")
    }
}

Het bovenstaande voorbeeld toont de standaardstructuur van XCTestCase. sut (System Under Test) — de naamsconventie voor het geteste object. MockURLSession vervangt het echte netwerk, waardoor UserService geïsoleerd kan worden getest. Het principe "één test — één controle" vereenvoudigt het debuggen: als een test faalt, weet de ontwikkelaar meteen welke functionaliteit defect is. Elke XCTestCase-test moet één scenario of één bewering controleren.

Asserts in XCTest

Basisgroep van asserts

XCTAssertTrue en XCTAssertFalse — basis-asserts voor het controleren van Booleaanse waarden. XCTAssertTrue(expression) slaagt als expression == true. XCTAssertEqual controleert de gelijkheid van twee waarden met ondersteuning voor alle typen die Equatable implementeren. Voor getallen met drijvende komma wordt XCTAssertEqual gebruikt met de parameter accuracy om rekening te houden met rekenfouten. Volgens Google Testing Blog (2024) dekt XCTAssertEqual 70% van alle controles in een typische testsuite.

Nil-asserts en fouten

XCTAssertNil en XCTAssertNotNil controleren optionele waarden op nil. Deze asserts zijn cruciaal voor Swift, waar optionele typen veel worden gebruikt. XCTAssertThrowsError controleert of de code een verwachte fout gooit. XCTUnwrap — een assert die een optionele waarde uitpakt en faalt met een duidelijke melding als de waarde nil is. Het vergelijken van strings via XCTAssertEqual gebruikt letterlijke vergelijking, niet semantische. XCTAssertNoThrow — de tegenhanger om te controleren dat code geen fout gooit.

AssertDoelVoorbeeld
XCTAssertEqualGelijkheid controlerenXCTAssertEqual(a, b)
XCTAssertTrueWaarheid controlerenXCTAssertTrue(result)
XCTAssertNilNil controlerenXCTAssertNil(error)
XCTAssertThrowsErrorFout controlerenXCTAssertThrowsError(try parse(""))
XCTUnwrapOptional uitpakkenXCTUnwrap(value)

XCTestExpectation en asynchrone tests

XCTestExpectation — is een mechanisme voor het testen van asynchrone code. De test maakt een verwachting met een beschrijvende naam, geeft deze door aan de asynchrone operatie en roept `wait(for:timeout:)` aan. Als de verwachting niet binnen de timeout wordt vervuld, faalt de test. Timeout is standaard 10 seconden, maar voor snelle operaties wordt aangeraden 1–3 seconden in te stellen om de totale testtijd te versnellen.

XCTWaiter — een flexibeler alternatief voor wait(for:timeout:). XCTWaiter maakt het mogelijk om op meerdere verwachtingen te wachten, de uitvoervolgorde in te stellen en timeouts programmatisch af te handelen. In tegenstelling tot wait retourneert XCTWaiter `XCTWaiter.Result`, dat kan worden geanalyseerd. De gedelegeerde XCTWaiterDelegate meldt schendingen van de verwachtingsvolgorde en timeouts.

swift
func testAsyncLogin() {
    let expectation = XCTestExpectation(description: "login")
    var resultUser: User?

    sut.login(email: "a@b.com", password: "123") { user in
        resultUser = user
        expectation.fulfill()
    }

    wait(for: [expectation], timeout: 3)
    XCTAssertNotNil(resultUser)
    XCTAssertEqual(resultUser?.email, "a@b.com")
}

In het voorbeeld wordt XCTestExpectation gebruikt voor het testen van asynchrone aanmelding. fulfill() wordt aangeroepen binnen de callback, wat aangeeft dat de asynchrone bewerking is voltooid. Als fulfill() niet binnen 3 seconden wordt aangeroepen, faalt de test met een timeout. Na het wachten worden de asserts uitgevoerd om het resultaat te controleren. Meerdere verwachtingen kunnen als array worden doorgegeven en er kan worden gewacht tot alle zijn voldaan.

Performantietests met XCTest

measure(metrics:) — de XCTestCase-methode voor het maken van performantietests. Het codeblok binnen measure wordt 10 keer achter elkaar uitgevoerd en XCTest verzamelt statistieken: gemiddelde tijd, mediaan, standaarddeviatie. Metrics — de array van gevolgde metrieken: XCTClockMetric (tijd), XCTMemoryMetric (geheugen), XCTStorageMetric (schijf) en XCTCPUMetric (processor). Volgens Apple WWDC (2024) zijn performantietests met XCTCPUMetric nuttig voor het detecteren van regressies in algoritmen.

Baseline (basislijn) voor performantietests wordt ingesteld in Xcode Test Plan. Als de uitvoeringstijd de baseline met een vastgesteld percentage (standaard 10%) overschrijdt, wordt de test als mislukt beschouwd. De baseline wordt handmatig bijgewerkt nadat is bevestigd dat de prestatieverandering wordt verwacht. Test Plan in Xcode maakt het mogelijk om performantietests te groeperen op configuratie: debug/release, verschillende apparaten, verschillende iOS-versies.

swift
func testArraySortPerformance() {
    let numbers = (1...10000).shuffled()
    measure(metrics: [XCTClockMetric()]) {
        let _ = numbers.sorted()
    }
}

Deze performantietest meet de sorteertijd van een array van 10000 elementen. XCTClockMetric registreert de werkelijke uitvoeringstijd. Als na het wijzigen van het sorteer-algoritme de tijd met 10% of meer toeneemt, zal de test op regressie wijzen. XCTest-performantietests zijn vooral nuttig voor: gegevensverwerkingsalgoritmen, het renderen van UI-componenten, databasebewerkingen en netwerkverzoeken.

Testorganisatie en CI/CD-integratie

Structuur van het testproject

De structuur van het testproject in XCTest volgt de conventie: één testbestand per klasse, geplaatst in een aparte `<TargetName>Tests`-directory. Bestandsnamen komen overeen met de namen van de geteste klassen met het achtervoegsel `Tests`: `UserService.swift` → `UserServiceTests.swift`. Test Targets in Xcode worden afzonderlijk geconfigureerd voor unit-tests en UI-tests, zodat ze onafhankelijk kunnen worden uitgevoerd. Schemes in Xcode beheren de buildconfiguratie en de set uit te voeren tests.

CI/CD-integratie

Xcode Cloud en GitHub Actions ondersteunen het uitvoeren van XCTest via `xcodebuild test -scheme App -testPlan SmokeTest`. De CI-pipeline omvat: build → unit-tests uitvoeren → UI-tests uitvoeren → rapport publiceren. Het JUnit-rapport wordt gegenereerd door `xcodebuild` met de optie `-resultBundlePath` en kan in elk CI-hulpmiddel worden geïmporteerd. Code Coverage — ingebouwde functie van XCTest die laat zien welke coderegels door tests worden gedekt. Minimale dekkingsdrempel voor productiecode — 70% voor kritieke bedrijfslogica.

Xcode Cloud en GitHub Actions ondersteunen het uitvoeren van XCTest via `xcodebuild test -scheme App -testPlan SmokeTest`. De CI-pipeline omvat: build → unit-tests uitvoeren → UI-tests uitvoeren → rapport publiceren. JUnit-rapport wordt gegenereerd door `xcodebuild` met de optie `-resultBundlePath` en kan in elk CI-hulpmiddel worden geïmporteerd. Bitrise en Jenkins hebben kant-en-klare stappen voor XCTest.

Code Coverage — ingebouwde functie van XCTest die laat zien welke coderegels door tests worden gedekt. Xcode toont de dekking in groen (gedekt), rood (niet gedekt) en geel (gedeeltelijk gedekt). Minimale drempel voor productiecode — 70% voor kritieke bedrijfslogica. Volgens Google Testing Blog (2024) leidt het afdwingen van 80% dekking voor alle modules tot het ontstaan van "lege tests" die de logica niet controleren, maar alleen code uitvoeren.

Veelgestelde vragen

Hoe verschilt XCTest van Quick en Nimble?

XCTest — het officiële Apple-framework met directe integratie in Xcode. Quick en Nimble — bibliotheken van derden die BDD-syntax en beter leesbare asserts bieden. Quick en Nimble zijn handig voor Acceptance Testing, maar XCTest is betrouwbaarder voor unit-tests vanwege het ontbreken van externe afhankelijkheden.

Hoe test je asynchrone code in XCTest?

Asynchrone code wordt getest via XCTestExpectation + `wait(for:timeout:)` of via de `async/await`-methoden van XCTest (iOS 13+). Voor callback-gebaseerde API's wordt een verwachting gemaakt die in de closure wordt aangeroepen. Voor async/await worden standaard-asserts gebruikt in async-functies.

Hoe mock je afhankelijkheden in XCTest?

XCTest bevat geen ingebouwd framework voor mocks. Mocken wordt gerealiseerd via protocollen: er wordt een mock-klasse gemaakt die hetzelfde protocol implementeert als de echte afhankelijkheid. Voor automatische mock-generatie worden Cuckoo, SwiftyMocky of handmatige mocks gebruikt. Dependency Injection via initializers — een verplichte voorwaarde voor testen.

Kan XCTest op echte apparaten worden uitgevoerd?

Ja, XCTest wordt op echte apparaten uitgevoerd via Xcode of xcodebuild met de parameter `-destination 'platform=iOS,name=iPhone 15'`. UI-tests op echte apparaten geven nauwkeurigere resultaten dan op simulatoren. Voor uitvoering op device farms worden BrowserStack, Sauce Labs of Firebase Test Lab gebruikt.

Wat is er nieuw in Swift Testing vergeleken met XCTest?

Swift Testing (2024) — een nieuw Apple-framework met de macro's `@Test`, `@Suite` en `@Expect`. Het biedt ingebouwde parametrisatie van tests, groepering in suites en een beter leesbare syntax. Swift Testing bestaat naast XCTest en vervangt het niet. XCTest blijft het belangrijkste framework voor UI-tests en performantietests.

Samenvatting

  • XCTest — het officiële Apple-framework voor testen, ingebouwd in Xcode en ondersteunend Swift en Objective-C.
  • XCTestCase biedt de levenscyclus setUp/tearDown en een set asserts voor unit-tests.
  • XCTestExpectation en XCTWaiter — mechanismen voor het testen van asynchrone code met callback en async/await.
  • Performantietests gebruiken measure(metrics:) met ondersteuning voor XCTClockMetric, XCTMemoryMetric en XCTCPUMetric.
  • Asserts — XCTAssertEqual, XCTAssertTrue, XCTAssertNil, XCTAssertThrowsError, XCTUnwrap en andere.
  • CI/CD-integratie via xcodebuild, Xcode Cloud en GitHub Actions met automatische rapportgeneratie.
  • Swift Testing — een nieuw Apple-framework dat XCTest aanvult voor unit-tests en parametrisatie.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook