Использовали ли вы XCTest для модульного тестирования в iOS?

Ответ

Да, активно использовал XCTest для модульного тестирования (Unit Testing).

Что тестировал:

  • Модели данных: Валидация, преобразования форматов, бизнес-правила.
  • Бизнес-логика: Сервисы, утилиты, менеджеры.
  • ViewModel/Presenter: Проверка корректности выходных состояний на основе входных данных и действий.

Пример теста для валидатора email:

import XCTest
@testable import MyApp

class EmailValidatorTests: XCTestCase {
    func testValidEmail() {
        let validator = EmailValidator()
        XCTAssertTrue(validator.isValid("user@example.com"))
    }

    func testInvalidEmail() {
        let validator = EmailValidator()
        XCTAssertFalse(validator.isValid("invalid-email"))
    }

    func testEmptyEmail() {
        let validator = EmailValidator()
        XCTAssertFalse(validator.isValid(""))
    }
}

Практики:

  • Мокирование: Использовал протоколы для создания mock-объектов зависимостей.
  • Асинхронность: Применял XCTestExpectation для тестирования асинхронного кода.
  • Покрытие: Стремился к покрытию ключевой логики на уровне 60-80%.

Ответ 18+ 🔞

О, так ты про тесты спрашиваешь? Ну да, я с этим XCTest на короткой ноге, конечно. Без этого сейчас никуда, иначе потом вылезет такое, что волосы дыбом встанут, а виноват будешь ты, потому что не проверил.

Что я там, блядь, мучил:

  • Модели данных: Чтобы вся эта валидация и преобразование форматов не выкинули какую-нибудь хуйню в продакшен. Бизнес-правила — святое, их надо гонять по полной.
  • Всякая бизнес-логика: Сервисы, утилиты, менеджеры — чтобы работали как швейцарские часы, а не как китайский будильник.
  • ViewModel/Presenter: Чтобы на основе одних данных получались правильные состояния, а не пиздец в интерфейсе. Проверял вход, выход и что между ними.

Вот, смотри, как я, например, валидатор почты тестил, чтоб он не пропускал всякую дичь:

import XCTest
@testable import MyApp

class EmailValidatorTests: XCTestCase {
    func testValidEmail() {
        let validator = EmailValidator()
        XCTAssertTrue(validator.isValid("user@example.com"))
    }

    func testInvalidEmail() {
        let validator = EmailValidator()
        XCTAssertFalse(validator.isValid("invalid-email"))
    }

    func testEmptyEmail() {
        let validator = EmailValidator()
        XCTAssertFalse(validator.isValid(""))
    }
}

А как я это делал, внатуре:

  • Мокирование, ёпта: Заводил протоколы и подсовывал вместо реальных зависимостей их подставные копии — моки. Чтобы тест гонял только то, что нужно, а не лез в сеть или базу.
  • Асинхронщина: Использовал XCTestExpectation, когда нужно было дождаться, пока вся эта асинхронная хуйня завершится. Иначе тест проскакивает, а ошибка вылезает потом.
  • Покрытие: Гнался за покрытием ключевой логики где-то на 60-80%. Стопроцентное — это, конечно, ебать какая сказка, но на практике часто овердохуища времени уходит за эти последние проценты. Главное — чтобы ядро было прикрыто, а не какая-нибудь хуйня вроде геттеров.