Как писать UI-тесты для iOS-приложения с помощью XCUITest?

«Как писать UI-тесты для iOS-приложения с помощью XCUITest?» — вопрос из категории Тестирование, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

XCUITest — это фреймворк Apple для сквозного (end-to-end) тестирования пользовательского интерфейса. Он симулирует действия реального пользователя (тапы, свайпы, ввод текста).

Базовый пример теста:

import XCTest

class LoginUITests: XCTestCase {
    func testSuccessfulLogin() {
        let app = XCUIApplication()
        app.launch()

        // 1. Находим элементы по accessibility идентификаторам (лучшая практика).
        let loginField = app.textFields["loginTextField"]
        let passwordField = app.secureTextFields["passwordTextField"]
        let loginButton = app.buttons["loginButton"]

        // 2. Взаимодействуем с элементами.
        loginField.tap()
        loginField.typeText("user@example.com")
        passwordField.tap()
        passwordField.typeText("password123")
        loginButton.tap()

        // 3. Проверяем ожидаемый результат.
        let welcomeLabel = app.staticTexts["welcomeLabel"]
        // Ожидаем появления элемента с таймаутом.
        XCTAssertTrue(welcomeLabel.waitForExistence(timeout: 3))
        XCTAssertEqual(welcomeLabel.label, "Добро пожаловать!")
    }
}

Критически важные практики:

  • Идентификаторы: Всегда используйте accessibilityIdentifier, а не текст или индекс, для поиска элементов. Это делает тесты устойчивыми к локализации и изменениям UI.
  • Page Object Pattern: Инкапсулируйте логику поиска и взаимодействия с экраном в отдельные классы (Page Objects). Это повышает переиспользование и поддерживаемость кода.

    class LoginPage {
        private let app: XCUIApplication
        init(app: XCUIApplication) { self.app = app }
    
        var loginField: XCUIElement { app.textFields["loginTextField"] }
        func login(username: String, password: String) { ... }
    }
  • Запуск и сброс: Настраивайте setUp() и tearDown() методы для чистого состояния перед каждым тестом.
  • Ожидания: Всегда используйте waitForExistence(timeout:) перед проверкой элементов, которые появляются асинхронно.
  • Запись: Инструмент записи тестов в Xcode помогает быстро создать каркас, но его нужно дорабатывать вручную для надежности.