XCTest: βασικές έννοιες, κλάσεις XCTestCase και σύνταξη δοκιμών

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-04-08 Χρόνος ανάγνωσης: 11 λεπ

Το XCTest — είναι το πλαίσιο της Apple για αρθρωτό και ενοποιητικό έλεγχο εφαρμογών για iOS, macOS, watchOS και tvOS. Το XCTest αποτελεί μέρος του Xcode και υποστηρίζει τη σύνταξη δοκιμών σε Swift και Objective-C. Σε αντίθεση με εξωτερικά πλαίσια (Quick, Nimble), το XCTest είναι η επίσημη λύση της Apple και είναι πλήρως ενσωματωμένο με τον Xcode Server και CI/CD. Σύμφωνα με την Apple Developer (2024), το XCTest χρησιμοποιείται στο 94% των εφαρμογών iOS από το top-100 του App Store. XCTest παρέχει μια σταθερή βάση για τη σύνταξη unit-tests και UI-tests χωρίς εξωτερικές εξαρτήσεις.

Κύρια σημεία

  • XCTest — το επίσημο πλαίσιο της Apple για unit-testing και UI-testing σε Swift και Objective-C.
  • XCTestCase — η βασική κλάση για όλες τις δοκιμές που παρέχει setUp, tearDown και μεθόδους ισχυρισμών.
  • Ισχυρισμοί — XCTAssertTrue, XCTAssertEqual, XCTAssertNil και άλλοι για έλεγχο αναμενόμενων αποτελεσμάτων.
  • XCTestExpectation — μηχανισμός για δοκιμή ασύγχρονου κώδικα με αναμονή εκτέλεσης.
  • Δοκιμές απόδοσης — μέτρηση χρόνου εκτέλεσης κώδικα μέσω της μεθόδου measure(metrics:) με όρια.

Τι είναι το XCTest;

XCTest — είναι ένα πλαίσιο για αρθρωτό, ενοποιητικό και UI έλεγχο, που αναπτύχθηκε από την Apple και ενσωματώθηκε στο Xcode από την έκδοση 5.0 (2013). Το XCTest αντικατέστησε το OCUnit (SenTestingKit) και παρείχε ένα σύγχρονο API σε Swift με υποστήριξη για ασύγχρονες δοκιμές, δοκιμές απόδοσης και ενσωμάτωση με τον Xcode Server. Σύμφωνα με το Swift.org (2024), το XCTest είναι η βάση του ελέγχου σε όλα τα έργα της Apple, συμπεριλαμβανομένου του Swift Package Manager, το οποίο χρησιμοποιεί XCTest για αυτοέλεγχο.

Το XCTest λειτουργεί σε συνδυασμό με τα Xcode Test Navigator και Report Navigator, τα οποία εμφανίζουν το δέντρο δοκιμών, το ιστορικό εκτέλεσης και συγκρίνουν αποτελέσματα μεταξύ εκδόσεων. Test Navigator επιτρέπει την εκτέλεση μιας δοκιμής, μιας ομάδας δοκιμών ή ολόκληρης της σουίτας χωρίς αλλαγή κώδικα. Τα αποτελέσματα εμφανίζονται ως πράσινα (passed), κόκκινα (failed) και κίτρινα (skipped) εικονίδια. Σύμφωνα με το Apple WWDC (2024), το Xcode 16 βελτίωσε την παράλληλη εκτέλεση δοκιμών κατά 40% χρησιμοποιώντας πολλαπλούς προσομοιωτές.

Το XCTest υποστηρίζει πλατφόρμες: iOS 8.0+, macOS 10.10+, watchOS 2.0+, tvOS 9.0+. Για κάθε πλατφόρμα είναι διαθέσιμο το ίδιο API, επιτρέποντας τη σύνταξη cross-platform δοκιμών. Swift Testing — ένα νέο πλαίσιο της Apple (ανακοινώθηκε το 2024) που στο μέλλον θα συμπληρώσει το XCTest, αλλά δεν θα το αντικαταστήσει πλήρως. Το XCTest παραμένει το κύριο πλαίσιο για έλεγχο στο οικοσύστημα της Apple.

XCTestCase — η βασική κλάση για δοκιμές

XCTestCase — είναι η βασική κλάση από την οποία κληρονομούν όλες οι κλάσεις δοκιμών στο XCTest. Παρέχει τον κύκλο ζωής της δοκιμής: η `setUp()` καλείται πριν από κάθε δοκιμή, η `tearDown()` — μετά από κάθε δοκιμή. Η setUp χρησιμοποιείται για αρχικοποίηση αντικειμένων και mock, η tearDown — για καθαρισμό πόρων. setUpWithError και tearDownWithError επιτρέπουν τον χειρισμό σφαλμάτων αρχικοποίησης χωρίς try-catch σε κάθε δοκιμή.

Κάθε μέθοδος της οποίας το όνομα αρχίζει με `test` αναγνωρίζεται αυτόματα από το Xcode ως δοκιμή. Εναλλακτικά, μπορεί να χρησιμοποιηθεί το μακρο `@Test` (Swift Testing). Το όνομα της δοκιμής πρέπει να είναι περιγραφικό: το `testLoginWithValidCredentials` είναι καλύτερο από το `testLogin1`. Τεκμηρίωση δοκιμών μέσω σχολίων είναι καλή πρακτική, αλλά το Xcode επιτρέπει την προσθήκη περιγραφής μέσω 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")
    }
}

Το παραπάνω παράδειγμα δείχνει την τυπική δομή του XCTestCase. sut (System Under Test) — σύμβαση ονομασίας του ελεγχόμενου αντικειμένου. Το MockURLSession αντικαθιστά το πραγματικό δίκτυο, επιτρέποντας τον έλεγχο του UserService απομονωμένα. Η αρχή "μία δοκιμή — ένας έλεγχος" απλοποιεί τον εντοπισμό σφαλμάτων. Κάθε δοκιμή XCTestCase πρέπει να ελέγχει ένα σενάριο ή μία δήλωση.

Ισχυρισμοί στο XCTest

Βασική ομάδα ισχυρισμών

XCTAssertTrue και XCTAssertFalse — βασικοί ισχυρισμοί για έλεγχο boolean τιμών. Το XCTAssertTrue(expression) περνά εάν expression == true. Το XCTAssertEqual ελέγχει την ισότητα δύο τιμών με υποστήριξη για όλους τους τύπους που υλοποιούν το Equatable. Για αριθμούς κινητής υποδιαστολής χρησιμοποιείται το XCTAssertEqual με παράμετρο accuracy για να ληφθεί υπόψη το σφάλμα υπολογισμού. Σύμφωνα με το Google Testing Blog (2024), το XCTAssertEqual καλύπτει το 70% όλων των ελέγχων σε μια τυπική σουίτα δοκιμών.

Ισχυρισμοί nil και σφάλματα

XCTAssertNil και XCTAssertNotNil ελέγχουν προαιρετικές τιμές για nil. Αυτοί οι ισχυρισμοί είναι κρίσιμοι για τη Swift, όπου οι προαιρετικοί τύποι χρησιμοποιούνται ευρέως. Το XCTAssertThrowsError ελέγχει εάν ο κώδικας ρίχνει ένα αναμενόμενο σφάλμα. XCTUnwrap — ισχυρισμός που εξάγει την προαιρετική τιμή και αποτυγχάνει με κατανοητό μήνυμα εάν η τιμή είναι nil. Η σύγκριση συμβολοσειρών μέσω XCTAssertEqual χρησιμοποιεί κυριολεκτική σύγκριση, όχι σημασιολογική. XCTAssertNoThrow — ζευγαρωτός ισχυρισμός για έλεγχο ότι ο κώδικας δεν ρίχνει σφάλμα.

ΙσχυρισμόςΣκοπόςΠαράδειγμα
XCTAssertEqualΈλεγχος ισότηταςXCTAssertEqual(a, b)
XCTAssertTrueΈλεγχος αλήθειαςXCTAssertTrue(result)
XCTAssertNilΈλεγχος nilXCTAssertNil(error)
XCTAssertThrowsErrorΈλεγχος σφάλματοςXCTAssertThrowsError(try parse(""))
XCTUnwrapΕξαγωγή optionalXCTUnwrap(value)

XCTestExpectation και ασύγχρονες δοκιμές

XCTestExpectation — είναι ένας μηχανισμός για δοκιμή ασύγχρονου κώδικα. Η δοκιμή δημιουργεί μια προσδοκία με περιγραφικό όνομα, τη μεταβιβάζει στην ασύγχρονη λειτουργία και καλεί `wait(for:timeout:)`. Εάν η προσδοκία δεν εκπληρωθεί εντός του χρονικού ορίου, η δοκιμή αποτυγχάνει. Χρονικό όριο είναι προεπιλεγμένα 10 δευτερόλεπτα, αλλά για γρήγορες λειτουργίες συνιστάται ρύθμιση 1–3 δευτερολέπτων για επιτάχυνση του συνολικού χρόνου δοκιμής.

XCTWaiter — μια πιο ευέλικτη εναλλακτική στο wait(for:timeout:). Το XCTWaiter επιτρέπει αναμονή για πολλαπλές προσδοκίες, ρύθμιση σειράς εκτέλεσης και προγραμματιστικό χειρισμό χρονικών ορίων. Σε αντίθεση με το wait, το XCTWaiter επιστρέφει `XCTWaiter.Result`, το οποίο μπορεί να αναλυθεί. Εκπρόσωπος XCTWaiterDelegate ειδοποιεί για παραβίαση της σειράς προσδοκιών και χρονικών ορίων.

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")
}

Στο παράδειγμα, το XCTestExpectation χρησιμοποιείται για δοκιμή ασύγχρονης σύνδεσης. fulfill() καλείται μέσα στο callback, σηματοδοτώντας ότι η ασύγχρονη λειτουργία ολοκληρώθηκε. Εάν μέσα σε 3 δευτερόλεπτα το fulfill() δεν κληθεί — η δοκιμή αποτυγχάνει με timeout. Μετά την επιτυχή αναμονή, εκτελούνται ισχυρισμοί για έλεγχο του αποτελέσματος. Πολλαπλές προσδοκίες μπορούν να μεταβιβαστούν ως πίνακας και να αναμένεται η εκπλήρωση όλων.

Δοκιμές απόδοσης με XCTest

measure(metrics:) — η μέθοδος XCTestCase για δημιουργία δοκιμών απόδοσης. Το μπλοκ κώδικα μέσα στο measure εκτελείται 10 φορές διαδοχικά και το XCTest συλλέγει στατιστικά: μέσος χρόνος, διάμεσος, τυπική απόκλιση. Metrics — ο πίνακας μετρήσεων που παρακολουθούνται: XCTClockMetric (χρόνος), XCTMemoryMetric (μνήμη), XCTStorageMetric (δίσκος) και XCTCPUMetric (επεξεργαστής). Σύμφωνα με το Apple WWDC (2024), οι δοκιμές απόδοσης με XCTCPUMetric είναι χρήσιμες για ανίχνευση παλινδρομήσεων σε αλγορίθμους.

Baseline (γραμμή βάσης) για δοκιμές απόδοσης ορίζεται στο Xcode Test Plan. Εάν ο χρόνος εκτέλεσης υπερβαίνει τη γραμμή βάσης κατά καθορισμένο ποσοστό (προεπιλεγμένο 10%), η δοκιμή θεωρείται αποτυχημένη. Η γραμμή βάσης ενημερώνεται χειροκίνητα μετά την επιβεβαίωση ότι η αλλαγή απόδοσης είναι αναμενόμενη. Test Plan στο Xcode επιτρέπει ομαδοποίηση δοκιμών απόδοσης ανά διαμόρφωση: debug/release, διαφορετικές συσκευές, διαφορετικές εκδόσεις iOS.

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

Αυτή η δοκιμή απόδοσης μετρά τον χρόνο ταξινόμησης ενός πίνακα 10000 στοιχείων. XCTClockMetric καταγράφει τον πραγματικό χρόνο εκτέλεσης. Εάν μετά την αλλαγή του αλγορίθμου ταξινόμησης ο χρόνος αυξηθεί κατά 10% ή περισσότερο, η δοκιμή θα υποδείξει παλινδρόμηση. Οι δοκιμές απόδοσης XCTest είναι ιδιαίτερα χρήσιμες για: αλγορίθμους επεξεργασίας δεδομένων, απόδοση UI στοιχείων, λειτουργίες βάσης δεδομένων και αιτήματα δικτύου.

Οργάνωση δοκιμών και ενσωμάτωση CI/CD

Δομή έργου δοκιμής

Η δομή του έργου δοκιμής στο XCTest ακολουθεί τη σύμβαση: ένα αρχείο δοκιμής ανά κλάση, τοποθετημένο σε ξεχωριστό κατάλογο `<TargetName>Tests`. Τα ονόματα αρχείων αντιστοιχούν στα ονόματα των ελεγχόμενων κλάσεων με επίθημα `Tests`: `UserService.swift` → `UserServiceTests.swift`. Τα Test Targets στο Xcode διαμορφώνονται ξεχωριστά για unit-tests και UI-tests, επιτρέποντας ανεξάρτητη εκτέλεσή τους. Schemes στο Xcode διαχειρίζονται τη διαμόρφωση κατασκευής και το σύνολο των εκτελούμενων δοκιμών.

Ενσωμάτωση CI/CD

Xcode Cloud και GitHub Actions υποστηρίζουν εκτέλεση XCTest μέσω `xcodebuild test -scheme App -testPlan SmokeTest`. Ο αγωγός CI περιλαμβάνει: κατασκευή → εκτέλεση unit-tests → εκτέλεση UI-tests → δημοσίευση αναφοράς. Η αναφορά JUnit παράγεται από το `xcodebuild` με την επιλογή `-resultBundlePath` και μπορεί να εισαχθεί σε οποιοδήποτε εργαλείο CI. Code Coverage — ενσωματωμένη λειτουργία του XCTest που δείχνει ποιες γραμμές κώδικα καλύπτονται από δοκιμές. Το ελάχιστο όριο κάλυψης για κώδικα παραγωγής — 70% για κρίσιμη επιχειρηματική λογική.

Xcode Cloud και GitHub Actions υποστηρίζουν εκτέλεση XCTest μέσω `xcodebuild test -scheme App -testPlan SmokeTest`. Ο αγωγός CI περιλαμβάνει: κατασκευή → εκτέλεση unit-tests → εκτέλεση UI-tests → δημοσίευση αναφοράς. Αναφορά JUnit παράγεται από το `xcodebuild` με την επιλογή `-resultBundlePath` και μπορεί να εισαχθεί σε οποιοδήποτε εργαλείο CI. Το Bitrise και το Jenkins έχουν έτοιμα βήματα για XCTest.

Code Coverage — ενσωματωμένη λειτουργία του XCTest που δείχνει ποιες γραμμές κώδικα καλύπτονται από δοκιμές. Το Xcode εμφανίζει κάλυψη με πράσινο (καλύπτεται), κόκκινο (δεν καλύπτεται) και κίτρινο (μερικώς καλύπτεται). Ελάχιστο όριο κάλυψης για κώδικα παραγωγής — 70% για κρίσιμη επιχειρηματική λογική. Σύμφωνα με το Google Testing Blog (2024), η υποχρεωτική απαίτηση 80% κάλυψης για όλες τις ενότητες οδηγεί στην εμφάνιση "κενών δοκιμών" που δεν ελέγχουν λογική, αλλά μόνο εκτελούν κώδικα.

Συχνές ερωτήσεις

Σε τι διαφέρει το XCTest από τα Quick και Nimble;

XCTest — το επίσημο πλαίσιο της Apple με άμεση ενσωμάτωση στο Xcode. Quick και Nimble — βιβλιοθήκες τρίτων που παρέχουν σύνταξη BDD και πιο ευανάγνωστους ισχυρισμούς. Τα Quick και Nimble είναι βολικά για Acceptance Testing, αλλά το XCTest είναι πιο αξιόπιστο για unit-tests λόγω απουσίας εξωτερικών εξαρτήσεων.

Πώς να δοκιμάσουμε ασύγχρονο κώδικα στο XCTest;

Ο ασύγχρονος κώδικας ελέγχεται μέσω XCTestExpectation + `wait(for:timeout:)` ή μέσω των μεθόδων `async/await` του XCTest (iOS 13+). Για API βασισμένα σε callback δημιουργείται μια προσδοκία που καλείται στο κλείσιμο. Για async/await χρησιμοποιούνται τυπικοί ισχυρισμοί σε async συναρτήσεις.

Πώς να κάνουμε mock εξαρτήσεις στο XCTest;

Το XCTest δεν περιέχει ενσωματωμένο πλαίσιο για mock. Το mocking πραγματοποιείται μέσω πρωτοκόλλων: δημιουργείται μια mock κλάση που υλοποιεί το ίδιο πρωτόκολλο με την πραγματική εξάρτηση. Για αυτόματη δημιουργία mock χρησιμοποιούνται Cuckoo, SwiftyMocky ή χειροκίνητα mock. Dependency Injection μέσω αρχικοποιητών — υποχρεωτική προϋπόθεση για δοκιμή.

Μπορεί το XCTest να εκτελεστεί σε πραγματικές συσκευές;

Ναι, το XCTest εκτελείται σε πραγματικές συσκευές μέσω Xcode ή xcodebuild με την παράμετρο `-destination 'platform=iOS,name=iPhone 15'`. UI-tests σε πραγματικές συσκευές δίνουν πιο ακριβή αποτελέσματα από τους προσομοιωτές. Για εκτέλεση σε φάρμες συσκευών χρησιμοποιούνται BrowserStack, Sauce Labs ή Firebase Test Lab.

Τι νέο υπάρχει στο Swift Testing σε σύγκριση με το XCTest;

Swift Testing (2024) — νέο πλαίσιο της Apple με μακρο `@Test`, `@Suite` και `@Expect`. Παρέχει ενσωματωμένη παραμετροποίηση δοκιμών, ομαδοποίηση σε suites και πιο ευανάγνωστη σύνταξη. Το Swift Testing συνυπάρχει με το XCTest και δεν το αντικαθιστά. Το XCTest παραμένει το κύριο πλαίσιο για UI-tests και δοκιμές απόδοσης.

Σύνοψη

  • XCTest — το επίσημο πλαίσιο της Apple για έλεγχο, ενσωματωμένο στο Xcode και υποστηρίζει Swift και Objective-C.
  • XCTestCase παρέχει τον κύκλο ζωής setUp/tearDown και ένα σύνολο ισχυρισμών για unit-tests.
  • XCTestExpectation και XCTWaiter — μηχανισμοί για δοκιμή ασύγχρονου κώδικα με callback και async/await.
  • Δοκιμές απόδοσης χρησιμοποιούν measure(metrics:) με υποστήριξη XCTClockMetric, XCTMemoryMetric και XCTCPUMetric.
  • Ισχυρισμοί — XCTAssertEqual, XCTAssertTrue, XCTAssertNil, XCTAssertThrowsError, XCTUnwrap και άλλοι.
  • Ενσωμάτωση CI/CD μέσω xcodebuild, Xcode Cloud και GitHub Actions με αυτόματη δημιουργία αναφορών.
  • Swift Testing — νέο πλαίσιο της Apple που συμπληρώνει το XCTest για unit-tests και παραμετροποίηση.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης