XCTest — är Apples ramverk för modulär och integrationstestning av applikationer för iOS, macOS, watchOS och tvOS. XCTest ingår i Xcode och stöder att skriva tester i Swift och Objective-C. Till skillnad från externa ramverk (Quick, Nimble) är XCTest Apples officiella lösning och är helt integrerad med Xcode Server och CI/CD. Enligt Apple Developer (2024) används XCTest i 94% av iOS-apparna från topp-100 App Store. XCTest ger en stabil grund för att skriva unit-tester och UI-tester utan externa beroenden.
Huvudpunkter
XCTest — är ett ramverk för modulär, integrations- och UI-testning, utvecklat av Apple och inbyggt i Xcode sedan version 5.0 (2013). XCTest ersatte OCUnit (SenTestingKit) och tillhandahöll ett modernt API i Swift med stöd för asynkrona tester, prestandatester och integration med Xcode Server. Enligt Swift.org (2024) är XCTest grunden för testning i alla Apple-projekt, inklusive Swift Package Manager, som använder XCTest för självtestning.
XCTest arbetar tillsammans med Xcode Test Navigator och Report Navigator, som visar testträdet, exekveringshistorik och jämför resultat mellan byggen. Test Navigator gör det möjligt att köra ett enskilt test, en grupp tester eller hela sviten utan att ändra kod. Resultaten visas som gröna (passed), röda (failed) och gula (skipped) ikoner. Enligt Apple WWDC (2024) förbättrade Xcode 16 parallelltestkörning med 40% genom att använda flera simulatorer.
XCTest stöder plattformar: iOS 8.0+, macOS 10.10+, watchOS 2.0+, tvOS 9.0+. För varje plattform finns samma API tillgängligt, vilket möjliggör skrivning av cross-platform-tester. Swift Testing — ett nytt Apple-ramverk (tillkännagivet 2024) som i framtiden kommer att komplettera XCTest, men inte helt ersätta det. XCTest förblir det huvudsakliga ramverket för testning i Apples ekosystem.
XCTestCase — är basklassen från vilken alla testklasser i XCTest ärver. Den tillhandahåller testets livscykel: `setUp()` anropas före varje test, `tearDown()` — efter varje test. SetUp används för initiering av objekt och mockar, tearDown — för rensning av resurser. setUpWithError och tearDownWithError möjliggör hantering av initieringsfel utan try-catch i varje test.
Varje metod vars namn börjar med `test` identifieras automatiskt av Xcode som ett test. Alternativt kan makrot `@Test` (Swift Testing) användas. Testnamnet bör vara beskrivande: `testLoginWithValidCredentials` är bättre än `testLogin1`. Dokumentation av tester via kommentarer är god praxis, men Xcode tillåter att lägga till beskrivning via User-Defined Attributes.
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")
}
}
Exemplet ovan visar standardstrukturen för XCTestCase. sut (System Under Test) — namnkonvention för det testade objektet. MockURLSession ersätter det verkliga nätverket och möjliggör testning av UserService isolerat. Principen "ett test — en kontroll" förenklar felsökning. Varje XCTestCase-test bör kontrollera ett scenario eller ett påstående.
XCTAssertTrue och XCTAssertFalse — grundläggande asserts för att kontrollera booleska värden. XCTAssertTrue(expression) godkänns om expression == true. XCTAssertEqual kontrollerar likheten mellan två värden med stöd för alla typer som implementerar Equatable. För flyttal används XCTAssertEqual med parametern accuracy för att ta hänsyn till beräkningsfel. Enligt Google Testing Blog (2024) täcker XCTAssertEqual 70% av alla kontroller i en typisk testsvit.
XCTAssertNil och XCTAssertNotNil kontrollerar optionella värden för nil. Dessa asserts är kritiska för Swift, där optionella typer används i stor utsträckning. XCTAssertThrowsError kontrollerar om koden kastar ett förväntat fel. XCTUnwrap — en assert som extraherar ett optionellt värde och misslyckas med ett tydligt meddelande om värdet är nil. Jämförelse av strängar via XCTAssertEqual använder bokstavlig jämförelse, inte semantisk. XCTAssertNoThrow — motsvarande assert för att kontrollera att koden inte kastar ett fel.
| Assert | Syfte | Exempel |
|---|---|---|
| XCTAssertEqual | Kontrollera likhet | XCTAssertEqual(a, b) |
| XCTAssertTrue | Kontrollera sanning | XCTAssertTrue(result) |
| XCTAssertNil | Kontrollera nil | XCTAssertNil(error) |
| XCTAssertThrowsError | Kontrollera fel | XCTAssertThrowsError(try parse("")) |
| XCTUnwrap | Extrahera optional | XCTUnwrap(value) |
XCTestExpectation — är en mekanism för att testa asynkron kod. Testet skapar en förväntan med ett beskrivande namn, skickar den till den asynkrona operationen och anropar `wait(for:timeout:)`. Om förväntan inte uppfylls inom tidsgränsen misslyckas testet. Tidsgräns är som standard 10 sekunder, men för snabba operationer rekommenderas 1–3 sekunder för att påskynda den totala testtiden.
XCTWaiter — ett mer flexibelt alternativ till wait(for:timeout:). XCTWaiter gör det möjligt att vänta på flera förväntningar, konfigurera exekveringsordning och hantera tidsgränser programmatiskt. Till skillnad från wait returnerar XCTWaiter `XCTWaiter.Result`, som kan analyseras. Delegat XCTWaiterDelegate meddelar om överträdelse av förväntansordning och tidsgränser.
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")
}
I exemplet används XCTestExpectation för att testa asynkron inloggning. fulfill() anropas inuti callbacken och signalerar att den asynkrona operationen är slutförd. Om fulfill() inte anropas inom 3 sekunder — misslyckas testet med timeout. Efter framgångsrik väntan utförs asserts för att kontrollera resultatet. Flera förväntningar kan skickas som en array och vänta på att alla uppfylls.
measure(metrics:) — XCTestCase-metoden för att skapa prestandatester. Kodblocket inuti measure körs 10 gånger i rad och XCTest samlar statistik: genomsnittlig tid, median, standardavvikelse. Metrics — arrayen av spårade mätvärden: XCTClockMetric (tid), XCTMemoryMetric (minne), XCTStorageMetric (disk) och XCTCPUMetric (processor). Enligt Apple WWDC (2024) är prestandatester med XCTCPUMetric användbara för att upptäcka regressioner i algoritmer.
Baseline (baslinje) för prestandatester ställs in i Xcode Test Plan. Om exekveringstiden överskrider baslinjen med en fastställd procentsats (standard 10%) anses testet misslyckat. Baslinjen uppdateras manuellt efter bekräftelse av att prestandaförändringen är förväntad. Test Plan i Xcode gör det möjligt att gruppera prestandatester efter konfiguration: debug/release, olika enheter, olika iOS-versioner.
func testArraySortPerformance() {
let numbers = (1...10000).shuffled()
measure(metrics: [XCTClockMetric()]) {
let _ = numbers.sorted()
}
}
Detta prestandatest mäter sorteringstiden för en array med 10000 element. XCTClockMetric registrerar den faktiska exekveringstiden. Om tiden efter ändring av sorteringsalgoritmen ökar med 10% eller mer kommer testet att indikera regression. XCTest-prestandatester är särskilt användbara för: databehandlingsalgoritmer, rendering av UI-komponenter, databasoperationer och nätverksförfrågningar.
Strukturen för testprojektet i XCTest följer konventionen: en testfil per klass, placerad i en separat `<TargetName>Tests`-katalog. Filnamnen motsvarar namnen på de testade klasserna med suffixet `Tests`: `UserService.swift` → `UserServiceTests.swift`. Test Targets i Xcode konfigureras separat för unit-tester och UI-tester, vilket möjliggör oberoende körning. Schemes i Xcode hanterar byggkonfigurationen och uppsättningen tester som körs.
Xcode Cloud och GitHub Actions stöder körning av XCTest via `xcodebuild test -scheme App -testPlan SmokeTest`. CI-pipelinen inkluderar: bygg → kör unit-tester → kör UI-tester → publicera rapport. JUnit-rapporten genereras av `xcodebuild` med alternativet `-resultBundlePath` och kan importeras till vilket CI-verktyg som helst. Code Coverage — inbyggd funktion i XCTest som visar vilka kodrader som täcks av tester. Minsta täckningströskel för produktionskod — 70% för kritisk affärslogik.
Xcode Cloud och GitHub Actions stöder körning av XCTest via `xcodebuild test -scheme App -testPlan SmokeTest`. CI-pipelinen inkluderar: bygg → kör unit-tester → kör UI-tester → publicera rapport. JUnit-rapport genereras av `xcodebuild` med alternativet `-resultBundlePath` och kan importeras till vilket CI-verktyg som helst. Bitrise och Jenkins har färdiga steg för XCTest.
Code Coverage — inbyggd funktion i XCTest som visar vilka kodrader som täcks av tester. Xcode visar täckning i grönt (täckt), rött (inte täckt) och gult (delvis täckt). Minsta tröskel för produktionskod — 70% för kritisk affärslogik. Enligt Google Testing Blog (2024) leder tvångskrav på 80% täckning för alla moduler till uppkomsten av "tomma tester" som inte kontrollerar logik, bara exekverar kod.
Vanliga frågor
XCTest — Apples officiella ramverk med direkt integration i Xcode. Quick och Nimble — tredjepartsbibliotek som tillhandahåller BDD-syntax och mer läsbara asserts. Quick och Nimble är bekväma för Acceptance Testing, men XCTest är mer pålitligt för unit-tester på grund av avsaknaden av externa beroenden.
Asynkron kod testas via XCTestExpectation + `wait(for:timeout:)` eller via `async/await`-metoder i XCTest (iOS 13+). För callback-baserade API:er skapas en förväntan som anropas i closure. För async/await används standard asserts i async-funktioner.
XCTest innehåller inget inbyggt ramverk för mockar. Mockning realiseras via protokoll: en mock-klass skapas som implementerar samma protokoll som det verkliga beroendet. För automatisk generering av mockar används Cuckoo, SwiftyMocky eller manuella mockar. Dependency Injection via initierare — obligatoriskt villkor för testning.
Ja, XCTest körs på riktiga enheter via Xcode eller xcodebuild med parametern `-destination 'platform=iOS,name=iPhone 15'`. UI-tester på riktiga enheter ger mer exakta resultat än på simulatorer. För körning på enhetsfarmar används BrowserStack, Sauce Labs eller Firebase Test Lab.
Swift Testing (2024) — ett nytt Apple-ramverk med makron `@Test`, `@Suite` och `@Expect`. Det erbjuder inbyggd parametrisering av tester, gruppering i sviter och mer läsbar syntax. Swift Testing samexisterar med XCTest och ersätter det inte. XCTest förblir det huvudsakliga ramverket för UI-tester och prestandatester.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också