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-додатків із топ-100 App Store. XCTest забезпечує стабільну основу для написання unit-тестів та UI-тестів без зовнішніх залежностей.

Головне

  • XCTest — офіційний фреймворк Apple для unit-тестування та UI-тестування на Swift і Objective-C.
  • XCTestCase — базовий клас для всіх тестів, який надає setUp, tearDown та методи асерцій.
  • Асерції — XCTAssertTrue, XCTAssertEqual, XCTAssertNil та інші для перевірки очікуваних результатів.
  • XCTestExpectation — механізм для тестування асинхронного коду з очікуванням виконання.
  • Performance тести — вимірювання часу виконання коду через метод measure(metrics:) з порогами.

Що таке XCTest?

XCTest — це фреймворк для модульного, інтеграційного та UI-тестування, розроблений Apple і вбудований в Xcode починаючи з версії 5.0 (2013 рік). XCTest замінив OCUnit (SenTestingKit) і надав сучасний API на Swift з підтримкою асинхронних тестів, performance-тестів та інтеграції з 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, що дозволяє писати кроссплатформні тести. Swift Testing — новий фреймворк від Apple (анонсований у 2024), який у майбутньому доповнить XCTest, але не замінить його повністю. XCTest залишається основним фреймворком для тестування в Apple-екосистемі.

XCTestCase — базовий клас для тестів

XCTestCase — це базовий клас, від якого успадковуються всі тестові класи в XCTest. Він надає життєвий цикл тесту: `setUp()` викликається перед кожним тестом, `tearDown()` — після кожного тесту. setUp використовується для ініціалізації об'єктів та моків, 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 — базові асерції для перевірки булевих значень. 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() не викликаний — тест падає з таймаутом. Після успішного очікування виконуються асерції для перевірки результату. Множинні очікування можна передавати масивом і очікувати виконання всіх.

Performance тести з XCTest

measure(metrics:) — метод XCTestCase для створення performance-тестів. Блок коду всередині measure запускається 10 разів поспіль, і XCTest збирає статистику: середній час, медіану, стандартне відхилення. Metrics — масив метрик, які відстежуються: XCTClockMetric (час), XCTMemoryMetric (пам'ять), XCTStorageMetric (диск) та XCTCPUMetric (процесор). За даними Apple WWDC (2024), performance-тести з XCTCPUMetric корисні для виявлення регресій в алгоритмах.

Baseline (базова лінія) для performance-тестів задається в Xcode Test Plan. Якщо час виконання перевищує baseline на встановлений відсоток (за замовчуванням 10%), тест вважається проваленим. Baseline оновлюється вручну після підтвердження, що зміна продуктивності є очікуваною. Test Plan в Xcode дозволяє групувати performance-тести за конфігураціями: debug/release, різні пристрої, різні версії iOS.

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

Даний performance-тест вимірює час сортування масиву з 10000 елементів. XCTClockMetric фіксує реальний час виконання. Якщо після зміни алгоритму сортування час збільшиться на 10% і більше, тест вкаже на регресію. Performance-тести XCTest особливо корисні для: алгоритмів обробки даних, рендерингу UI-компонентів, операцій з базою даних та мережевих запитів.

Організація тестів та CI/CD інтеграція

Структура тестового проєкту

Структура тестового проєкту в XCTest слідує угоді: один тестовий файл на один клас, розміщується в окремій директорії `<TargetName>Tests`. Імена файлів відповідають іменам тестованих класів із суфіксом `Tests`: `UserService.swift` → `UserServiceTests.swift`. Test Targets в Xcode налаштовуються окремо для unit-тестів та UI-тестів, що дозволяє запускати їх незалежно. Schemes в Xcode керують конфігурацією збірки та набором запускаємих тестів.

CI/CD інтеграція

Xcode Cloud та GitHub Actions підтримують запуск XCTest через `xcodebuild test -scheme App -testPlan SmokeTest`. CI-пайплайн включає: збірка → запуск unit-тестів → запуск UI-тестів → публікація звіту. JUnit звіт генерується через `xcodebuild` з опцією `-resultBundlePath` і може бути імпортований в будь-який CI-інструмент. Code Coverage — вбудована функція XCTest, яка показує, які рядки коду покриті тестами. Мінімальний поріг покриття для production-коду — 70% для критичної бізнес-логіки.

Xcode Cloud та GitHub Actions підтримують запуск XCTest через `xcodebuild test -scheme App -testPlan SmokeTest`. CI-пайплайн включає: збірка → запуск unit-тестів → запуск UI-тестів → публікація звіту. JUnit звіт генерується через `xcodebuild` з опцією `-resultBundlePath` і може бути імпортований в будь-який CI-інструмент. Bitrise та Jenkins мають готові кроки для XCTest.

Code Coverage — вбудована функція XCTest, яка показує, які рядки коду покриті тестами. Xcode відображає покриття зеленим (покрито), червоним (не покрито) та жовтим (частково покрито). Мінімальний поріг покриття для production-коду — 70% для критичної бізнес-логіки. За даними Google Testing Blog (2024), примусова вимога 80% покриття для всіх модулів призводить до появи «порожніх тестів», які не перевіряють логіку, а тільки виконують код.

Часто задавані питання

Чим XCTest відрізняється від Quick та Nimble?

XCTest — офіційний фреймворк Apple з прямою інтеграцією в Xcode. Quick та Nimble — сторонні бібліотеки, які надають BDD-синтаксис та більш читаємі асерції. Quick та Nimble зручні для Acceptance Testing, але XCTest надійніший для unit-тестів через відсутність зовнішніх залежностей.

Як тестувати асинхронний код в XCTest?

Асинхронний код тестується через XCTestExpectation + `wait(for:timeout:)` або через `async/await` методи XCTest (iOS 13+). Для callback-based API створюється очікування, яке викликається в замиканні. Для async/await використовуються стандартні асерції в async-функціях.

Як мокувати залежності в XCTest?

XCTest не містить вбудованого фреймворку для моків. Мокінг реалізується через протоколи: створюється mock-клас, що реалізує той самий протокол, що й реальна залежність. Для автоматичної генерації моків використовуються Cuckoo, SwiftyMocky або ручні моки. Dependency Injection через ініціалізатори — обов'язкова умова для тестування.

Чи можна запускати XCTest на реальних пристроях?

Так, XCTest запускається на реальних пристроях через Xcode або xcodebuild з параметром `-destination 'platform=iOS,name=iPhone 15'`. UI-тести на реальних пристроях дають більш точні результати, ніж на симуляторах. Для запуску на фермах пристроїв використовуються BrowserStack, Sauce Labs або Firebase Test Lab.

Що нового в Swift Testing порівняно з XCTest?

Swift Testing (2024) — новий фреймворк Apple з макросами `@Test`, `@Suite` та `@Expect`. Він надає вбудовану параметризацію тестів, групування в suite-и та більш читаємий синтаксис. Swift Testing співіснує з XCTest і не замінює його. XCTest залишається основним фреймворком для UI-тестів та performance-тестів.

Підсумки

  • XCTest — офіційний фреймворк Apple для тестування, вбудований в Xcode та підтримуючий Swift і Objective-C.
  • XCTestCase надає життєвий цикл setUp/tearDown та набір асерцій для unit-тестів.
  • XCTestExpectation та XCTWaiter — механізми для тестування асинхронного коду з callback та async/await.
  • Performance тести використовують measure(metrics:) з підтримкою XCTClockMetric, XCTMemoryMetric та XCTCPUMetric.
  • Asserts — XCTAssertEqual, XCTAssertTrue, XCTAssertNil, XCTAssertThrowsError, XCTUnwrap та інші.
  • CI/CD інтеграція через xcodebuild, Xcode Cloud та GitHub Actions з автоматичною генерацією звітів.
  • Swift Testing — новий фреймворк Apple, який доповнює XCTest для unit-тестів та параметризації.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також