Покрытие кода тестами в Swift: от «просто тестов» к реальной уверенности
Вы когда-нибудь удаляли «мёртвый» код, а через неделю падала сборка? Или гордились 90% покрытия, но баги всё равно вылезали в продакшне? Если да — эта статья для вас. Покрытие кода тестами (code coverage) в Swift — это не просто галочка для CI/CD. Это ваш персональный радар: он показывает, какие участки кода реально проверяются, а какие живут «на честном слове».
Мы не будем говорить о том, что тесты писать надо — это и так очевидно. Вместо этого разберём: как заставить Xcode считать покрытие правильно, какие метрики реально важны (спойлер: не только процент), и почему 100% покрытие — это часто ловушка. Поехали!
1. Зачем измерять покрытие кода в Swift?
Представьте, что вы — капитан корабля, а тесты — это ваши штурманы. Покрытие кода — это карта, на которой отмечены «исследованные» участки. Если карта белая — вы плывёте вслепую. Если на ней 100% закрашено, но без учёта граничных случаев — вы всё равно рискуете сесть на мель.
Основные причины внедрять code coverage Swift:
- Поиск «серых зон» — вы сразу видите, какой код никогда не выполняется в тестах (например, обработчики ошибок).
- Рефакторинг без страха — высокая метрика покрытия даёт зелёный свет на переписывание модулей.
- Документирование требований — тесты становятся живой документацией, а покрытие показывает, насколько эта документация полна.
- Контроль качества в CI — можно запретить мерж, если покрытие упало ниже порога (например, 80%).
Но запомните главное: покрытие кода тестами Swift — это метрика, а не цель. Гнаться за цифрой ради цифры — путь к бесполезным тестам, которые проверяют геттеры и сеттеры.
2. Инструменты для измерения покрытия в Swift: от Xcode до сторонних утилит
Экосистема Swift предлагает несколько способов получить заветные проценты. Рассмотрим три основных: встроенный Xcode, утилита slather и сервисы CI (например, Codecov).
2.1. Встроенный Xcode Code Coverage
Самый простой и доступный инструмент. Всё, что нужно — включить опцию Gather coverage data в схеме сборки (Product → Scheme → Edit Scheme → Test → Options). После прогона тестов откройте Report Navigator (Cmd+9) и выберите вкладку Coverage.
Плюсы: не требует установки, интеграция с Xcode, показывает покрытие в реальном времени.
Минусы: не умеет объединять результаты нескольких таргетов, сложно экспортировать в CI.
Пример настройки для проекта:
// В схеме сборки (Test → Options) ставим галочку "Code Coverage"
// После запуска тестов (Cmd+U) смотрим отчёт в Report Navigator.
// Для каждого файла видим строку с процентом покрытия и подсвеченные строки:
// зелёные — выполнены, красные — не выполнены.
2.2. Slather — гибкий генератор отчётов
Slather — это open-source утилита, которая парсит .profdata файлы (результаты покрытия Xcode) и генерирует красивые HTML-отчёты, XML (Cobertura) или даже JSON. Она особенно полезна, если вы используете CI и хотите сохранять историю покрытия.
Установка через CocoaPods или Homebrew:
# Homebrew
brew install slather
# CocoaPods (в Podfile)
pod 'slather'
Пример конфигурации .slather.yml:
# .slather.yml
coverage_service: cobertura_xml
xcodeproj: MyApp.xcodeproj
scheme: MyApp
source_directory: MyApp/Sources
output_directory: reports
ignore:
- MyAppTests/*
- Pods/*
Запуск:
slather coverage -s --scheme MyApp MyApp.xcodeproj
Плюсы: гибкость, поддержка разных форматов, интеграция с CI.
Минусы: требует дополнительной настройки, не обновляется под новые версии Xcode моментально.
2.3. Codecov / Coveralls — облачные сервисы
Если у вас большая команда, стоит подключить Codecov или Coveralls. Они автоматически комментируют PR, рисуют графики и показывают, какие файлы упали в покрытии.
Пример интеграции с GitHub Actions:
# .github/workflows/test.yml
name: Test and Coverage
on: [push]
jobs:
test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 14' -enableCodeCoverage YES
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
3. Какие метрики покрытия кода реально важны?
Когда вы видите в отчёте 85% покрытия, что это значит? Может быть, все 85% приходятся на лёгкие функции, а сложная бизнес-логика осталась за бортом. Поэтому используйте не один процент, а несколько метрик.
3.1. Line Coverage (покрытие строк)
Самая популярная метрика. Показывает, сколько строк исходного кода было выполнено хотя бы раз. Но она обманчива: одна строка может содержать несколько условий, и если одно из них не проверено — вы об этом не узнаете.
3.2. Branch Coverage (покрытие ветвлений)
Вот где начинается магия! Эта метрика считает, сколько логических путей (if/else, switch, тернарные операторы) было протестировано. Например, функция с if a > 0 { ... } else { ... } имеет две ветки. Если вы проверили только a > 0, branch coverage будет 50%, даже если line coverage — 100%.
В Xcode branch coverage не показывается по умолчанию, но его можно получить через slather или llvm-cov. Пример:
func discount(amount: Double, isPremium: Bool) -> Double {
var finalAmount = amount
if amount > 100 { // ветка 1
finalAmount *= 0.9
} else { // ветка 2
finalAmount *= 0.95
}
if isPremium { // ветка 3
finalAmount *= 0.8
}
return finalAmount
}
// Тест покрывает только amount > 100 и isPremium = true
// Branch coverage = 2 из 3 = 66%
3.3. Function Coverage (покрытие функций)
Процент функций, которые были