Quick/Nimble для Swift: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое Quick/Nimble и зачем они нужны?

Quick и Nimble — это две взаимодополняющие библиотеки для языка Swift, предназначенные для написания тестов в стиле Behavior-Driven Development (BDD). BDD — это методология разработки, которая фокусируется на поведении приложения с точки зрения пользователя, а не на технических деталях реализации. Вместо сухих утверждений типа XCTAssertEqual(a, b) вы описываете, что должно произойти: "когда пользователь нажимает кнопку, приложение должно показать приветствие".

Quick — это фреймворк для описания тестовых сценариев. Он предоставляет DSL (Domain-Specific Language), который позволяет структурировать тесты в виде иерархии: describe (описание контекста), context (уточнение условий) и it (конкретный тест). Это делает тесты читаемыми и понятными даже для нетехнических участников команды (менеджеров, аналитиков).

Nimble — это библиотека для написания матчеров (assertions). Вместо стандартных XCTAssert вы используете выразительные конструкции: expect(value).to(equal(expected)), expect(array).to(contain(element)). Nimble предоставляет десятки встроенных матчеров и позволяет легко создавать свои собственные.

Зачем это нужно?

  • Читаемость: Тесты на BDD выглядят как спецификация. Вы можете показать их заказчику и убедиться, что требования поняты правильно.
  • Структурированность: Иерархия describe/context/it помогает организовать тесты логически, избегая "спагетти-кода".
  • Выразительные матчеры: Nimble делает утверждения более наглядными и информативными. Ошибки в тестах становятся понятными сразу.
  • Удобство отладки: При падении теста вы видите не просто "ожидалось 5, получено 3", а полный контекст: "Когда пользователь вводит неверный пароль, система должна показать ошибку".

Установка Quick/Nimble

Quick и Nimble устанавливаются через CocoaPods или Swift Package Manager (SPM). Мы рассмотрим оба способа.

Установка через CocoaPods

Если вы еще не используете CocoaPods, установите его:

sudo gem install cocoapods

В корне вашего проекта создайте файл Podfile и добавьте в него:

target 'YourAppName' do
  # Обычные зависимости (если есть)
  use_frameworks!

  target 'YourAppNameTests' do
    inherit! :search_paths
    pod 'Quick'
    pod 'Nimble'
  end
end

Затем выполните:

pod install

После этого откройте .xcworkspace файл.

Установка через Swift Package Manager (SPM)

В Xcode: File → Add Packages... и введите URL репозитория:

https://github.com/Quick/Quick.git
https://github.com/Quick/Nimble.git

Выберите последнюю стабильную версию и добавьте пакеты к вашей тестовой цели (YourAppNameTests).

Быстрый старт: первый тест

Создадим простой класс Calculator и протестируем его.

Класс для тестирования

// Calculator.swift
import Foundation

class Calculator {
    func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }

    func subtract(_ a: Int, _ b: Int) -> Int {
        return a - b
    }

    func multiply(_ a: Int, _ b: Int) -> Int {
        return a * b
    }
}

Тест с использованием Quick и Nimble

// CalculatorSpec.swift
import Quick
import Nimble
@testable import YourAppName // Замените на имя вашего приложения

class CalculatorSpec: QuickSpec {
    override func spec() {
        describe("Calculator") {
            var calculator: Calculator!

            beforeEach {
                calculator = Calculator()
            }

            context("when adding two numbers") {
                it("should return the correct sum") {
                    let result = calculator.add(3, 5)
                    expect(result).to(equal(8))
                }
            }

            context("when subtracting two numbers") {
                it("should return the correct difference") {
                    let result = calculator.subtract(10, 4)
                    expect(result).to(equal(6))
                }
            }

            context("when multiplying two numbers") {
                it("should return the correct product") {
                    let result = calculator.multiply(4, 5)
                    expect(result).to(equal(20))
                }
            }
        }
    }
}

Запустите тесты (Cmd+U). Вы увидите зеленые галочки, если все прошло успешно.

Основные методы и функции Quick

1. describe

Что делает: Создает группу тестов, описывающую определенный класс, модуль или функциональность. Это корневой элемент иерархии.

Параметры: _ description: String — описание группы; closure: () -> Void — замыкание с тестами.

Пример:

describe("UserManager") {
    // Тесты для UserManager
}

2. context

Что делает: Уточняет состояние или условие, в котором выполняются тесты. Позволяет описать сценарии: "когда пользователь авторизован", "когда данных нет".

Параметры: _ description: String — описание контекста; closure: () -> Void — замыкание.

Пример:

context("when user is logged in") {
    it("should show profile") { ... }
}

3. it

Что делает: Определяет конкретный тест. Внутри пишутся утверждения (expectations).

Параметры: _ description: String — описание теста; closure: () throws -> Void — замыкание с тестом.

Пример:

it("should return the correct sum") {
    expect(calculator.add(2, 2)).to(equal(4))
}

4. beforeEach / afterEach

Что делает: Выполняют код перед/после каждым тестом в текущей группе. Используются для инициализации и очистки состояния.

Параметры: closure: () throws -> Void.

Пример:

beforeEach {
    calculator = Calculator()
}
afterEach {
    calculator = nil
}

5. beforeSuite / afterSuite

Что делает: Выполняются один раз до/после всех тестов в файле. Полезны для настройки глобальных ресурсов (база данных, моки).

Параметры: closure: () throws -> Void.

Пример:

beforeSuite {
    setupDatabase()
}
afterSuite {
    tearDownDatabase()
}

6. pending

Что делает: Помечает тест как "ожидающий". Такой тест не выполняется, но отображается в отчете. Удобно для планирования.

Параметры: _ description: String; closure: () throws -> Void (необязательно).

Пример:

pending("should handle network errors") {
    // TODO: написать тест
}

7. xit

Что делает: Временно отключает тест. Аналогично pending, но более явно.

Параметры: Те же, что у it.

Пример:

xit("should handle edge case") {
    // Этот тест не запустится
}

Основные методы и функции Nimble

8. expect(...).to(equal(...))

Что делает: Проверяет равенство значений. Самый базовый матчер.

Параметры: expect(_ expression: T) — выражение; .to(equal(_ expected: T)) — ожидаемое значение. T должен соответствовать Equatable.

Пример:

expect(result).to(equal(42))
expect(name).to(equal("Alice"))

9. expect(...).to(beTrue()) / beFalse()

Что делает: Проверяет, что выражение является true или false.

Параметры: Нет.

Пример:

expect(isValid).to(beTrue())
expect(hasError).to(beFalse())

10. expect(...).to(beNil()) / beNil()

Что делает: Проверяет, что значение равно nil или не равно nil.

Параметры: Нет.

Пример:

expect(optionalValue).to(beNil())
expect(nonOptionalValue).toNot(beNil())

11. expect(...).to(contain(...))

Что делает: Проверяет, что коллекция (массив, строка) содержит элемент.

Параметры: contain(_ elements: T...) — элементы для поиска.

Пример:

expect([1, 2, 3]).to(contain(2))
expect("Hello").to(contain("ell"))

12. expect(...).to(beCloseTo(...))

Что делает: Проверяет, что число близко к ожидаемому с заданной точностью. Полезно для чисел с плавающей точкой.

Параметры: beCloseTo(_ expected: Double, within delta: Double = 0.001).

Пример:

expect(0.1 + 0.2).to(beCloseTo(0.3, within: 0.0001))

13. expect(...).to(throwError())

Что делает: Проверяет, что замыкание выбрасывает ошибку. Можно уточнить тип ошибки.

Параметры: throwError(_ error: Error? = nil) — опциональная конкретная ошибка.

Пример:

expect {
    try divide(5, 0)
}.to(throwError(DivisionError.divisionByZero))

14. expect(...).to(satisfyAnyOf(...))

Что делает: Проверяет, что выражение удовлетворяет хотя бы одному из переданных матчеров. Аналог логического "ИЛИ".

Параметры: satisfyAnyOf(_ matchers: Matcher...).

Пример:

expect(value).to(satisfyAnyOf(equal(1), equal(2)))

15. expect(...).to(allPass(...))

Что делает

Рекомендаций