Привет, меня зовут Сергей, и я тестирую iOS приложения в Exness. В конце июня 2020 г. закончилась очередная WWDC. Давайте разберемся, что же она принесла нового в мир тестирования iOS приложений.

image

Но вначале краткий исторический экскурс: Apple WWDC (WorldWide Developers Conference), или просто даб-даб, это конфа, которую Apple с конца восьмидесятых проводит в Калифорнии. В этом году конференция впервые прошла в онлайн-формате. И если раньше билеты разыгрывались в лотерее, и тем, кто не получил желанного email, оставалось довольствоваться видео с сайта https://developer.apple.com/videos/, то в этом году по понятным причинам других вариантов не было: видео смотрели все.  

Итак, что же там можно было высмотреть по тестированию?

Сразу оговорюсь, что на WWDC 2020 не было какой-то большой общей сессии, посвященной тестированию в экосистеме Apple, как в прошлые годы (Testing in Xcode 2019 и What’s new in testing 2018, 2017). Новинки тестирования в 2020 размазали на шесть мини-сессий. Поехали!

XCTSkip для ваших тестов


В Xcode 11.4 добавили новый API для управления запуском тестов в зависимости от условий — XCTSkip. 

Часто в тестах, особенно интеграционных, есть условия или требования, которые нелегко замокать. Например, приложение имеет какой-то специфический функционал для айпада, который не работает на айфоне. Или какие-то фичи для конкретной версии операционной системы.

И раньше, когда тесты доходили до подобных кейсов (проверка айпад-only функционала на iPhone), стоял выбор:

  • Закончить выполнение тестового набора;
  • Пометить тест как пройденный и пойти дальше;
  • Зафейлить тест.

Теперь у нас есть ошибка, при возникновении которой текущий тест перестает выполняться и помечается как пропущенный. 

Таким образом, теперь в XCTest стало три статуса для пройденного теста вместо двух:

image

Подробнее тут и тут.

Обработка прерываний и алертов в UI тестах


Обработка прерываний и алертов была в XCTest и раньше, однако в сессии механизм его работы был раскрыт более подробно. Мне показалась интересной новая функциональность, добавленная в Xcode 11.4, iOS/tvOS 13.4 и macOS 10.15.4, а именно, сброс пермишенов (aka protected resources).

Суть в следующем: если раньше вы, например, в тесте #1 дали приложению доступ к камере или контактам, то потом, в тесте #2, #n этот доступ так просто не отобрать. Чтобы сделать это, придется переустанавливать приложение.

Теперь с помощью API для сброса авторизации для protected resources можно отобрать ранее выданный доступ: 

Class XCUIApplication {

	open func resetAuthorizationStatus(for: XCUIProtectedResource)

}

Сброс настроек для пермишенов заставляет приложение вести себя так, как будто оно ни разу до этого не запрашивало у пользователя доступ к protected resources.

Это позволяет пройти все пути с выдачей и забором пермишенов для контактов, календаря, фото, микрофона, камеры и геолокации. На iOS еще дополнительно можно сбросить доступ к Bluetooth и Keyboard network access, а начиная с Xcode 12 / iOS 14, к  данным Health. На Mac OS можно сбросить доступ к директориям Desktop и Downloads.

Ниже пример, как сбросить доступ приложения к фото:


// Example
func testAddingPhotosFirstTime() throws {
	let app = XCUIApplication()
	app.resetAuthorizationStatus(for: .photos)

	app.launch()

	// Test code...
}

Важно помнить, что часто (но не всегда) при сбросе пермишенов приложение убивается.

Подробнее тут, тут и тут.

Устраняем лаги анимации с помощью XCTest


Лаги анимации, или hitches —  такое поведение, когда фрейм появляется позже, чем ожидалось.
В лекции рассказано, как предотвратить появление лагов анимации вашего приложения путем замеров и тестирования с помощью Performance XCTests.

Также приводятся лучшие практики, и определено, какие лаги терпимы, а на какие стоит обратить внимание:

image

Описывается, почему Critical лаги заслуживают тщательного расследования и исправления. Сама тема тестирования анимации довольно обширна и достойна отдельной статьи, поэтому ограничимся вводной частью и ссылкой на первоисточник.

Триаж и диагностика упавших тестов


Часто починка упавших тестов это боль, которая занимает много времени и ресурсов.
В Xcode 12 появится новое API, которое должно облегчить починку упавших тестов. API должно помочь быстрее ответить на вопросы: что, как, почему и самое главное — где упало?

Если раньше после того, как тест упал, приходилось искать место падения в
Issue navigator или report navigator, то с Xcode 12 процесс поиска упростился: теперь место падения подсвечивается в самом тесте. 

Ошибка с выделением серым цветом появляется, если строка обращается к какой-то другой строке в дальнейшем:

image
 
И красным цветом, если ошибка произошла непосредственно в этой строке:

image

Удобная новая фича — открытие редактора кода не в отдельном окне, а прямо в report navigator:

image

Кроме того, в Xcode 12 добавился новый объект XCTIssue, который, помимо того, что инкапсулировал в себя данные об ошибках, которые ранее собирал в себе XCTest (сообщение, путь, номер строки и флаг «Expected»), теперь добавляет:
 
  • Distinct types;
  • Detailed description;
  • Associated error;
  • Attachments.

Подробнее тут и тут.

Пишите тесты для того, чтобы они падали 


Цель тестировщиков — писать тесты, чтобы увидеть их зелеными, пройденными, ведь это означает, что продукт можно отгружать конечным пользователям. Тем не менее, писать тесты, чтобы увидеть их зафейлеными тоже нужно, потому что зафейленный тест это с большой вероятностью — найденный баг. Таким образом, писать тесты нужно с оглядкой на то, что в случае, если они упадут, у нас было бы достаточно информации для расследования.
Итак, что же предлагается:

Используйте человекочитаемые сообщения в ассерt`ах:

image

Убедитесь, что используйте подходящий для вашей ситуации тип ассерt`а:

image

Unwrap'те optional'ы, чтобы ваши тесты падали, выбрасывая ошибку, а не крашились. Swift предоставляет несколько способов для этого, но в тестах, как правило, используют XCTUnwrap, который являет собой упрощение конструкции guard let.

image

Используйте waitForExistence() вместо sleep() для асинхронных ожиданий.

Используйте XCTContext.runActivity() для повышения читабельности лога выполнения теста:

image

И если хотите добавить дополнительное логирование, можно добавить вложение, приложить скриншот или вывод дебагера, как здесь. Это функция особенно полезна, если ваши тесты запускаются в CI/CD.

image

Подробнее тут.

Получайте результат тест рана быстрее


Обидно, когда утром в понедельник вы обнаруживаете, что запущенная в пятницу вечером длинная джоба так и не отработала до конца, зависнув на середине или вообще в самом начале. И вам предстоит начать рабочую неделю с разбора полетов: почему это произошло? Как избежать подобной ситуации в будущем? Как я мог спустить девять тысяч на коктейли за один вечер?

В Xcode 12 появились инструменты для защиты от зависаний. Это новая опция тест плана Execution Time Allowance.

Когда опция включена, Xcode устанавливает временной лимит исполнения каждого теста.
Если лимит превышен, Xcode делает следующее:

  1. Собирает отчет (spindump);
  2. Убивает зависший тест;
  3. Перезапускает тест раннер, чтобы оставшаяся часть сьюта смогла выполниться.

В отчете (spindump) отображается, какой из тредов, какой функции потратил наибольшее количество времени. Это позволит вам увидеть глазами бутылочное горлышко ваших тестов еще до того, как остынет ваш утренний кофе/ чай.

По дефолту на каждый тест выделяется по 10 минут, и если тест завершился быстрее, то для следующего теста таймер обнуляется. Если вам нужно больше/ меньше времени на каждый тест вашего тест сьюта, то можно изменить дефолтную величину в настройках тест плана. 

image

Еще это можно сделать опцией команды xcodebuild:

xcodebuild option
-default-test-execution-time-allowance <seconds>

Аналогично можно задать максимальное время выполнения теста:

image

xcodebuild option
-maximun-test-execution-time-allowance <seconds>

Даже если вам нужно задать время выполнения для какого-то конкретного теста или тестового класса, то это тоже возможно с помощью executionTimeAllowance API:

Class XCTestCase: XCTest {
	var executionTimeAllowance: TimeInterval // с округлением до минуты
}

Точная настройка выполнения того или иного теста позволит вам сэкономить время, но и это еще не все, что можно сделать для ускорения прохождения длинного тест сьюта.

Xcode 12 позволяет запускать тесты на нескольких девайсах одновременно. Эту фичу назвали Parallel Distributed Testing. Польза от запуска тестов на нескольких девайсах очевидна — приличная экономия времени.

image

image

Но, к сожалению, есть и подводные камни: порядок запуска тестов в параллели не детерминирован, нет никакой гарантии, что на девайсе #1 после теста номер 5, будет выполнен тест номер 6. Этот факт обязательно нужно учитывать при планировании запуска тестов с помощью Parallel Distributed Testing.

Вообще идея запусков тестов в параллели не нова. Такая возможность была и до Xcode 12, но именно в Xcode 12 появилась возможность запускать тесты на реальных девайсах (пока только с помощью  xcodebuild).

Команда для запуска параллельных распределенных тестов выглядит следующим образом: 

xcodebuild test
    -project MyProject.xcodeproj
    -scheme MyProject
    -parallel-testing-enabled YES
    -parallelize-test-among-desinations
    -destination 'platform=iOS,name=iPhone 11'
    -destination 'platform=iOS,name=iPad pro' 

Подробнее тут.

На этом обзор новых тесто-фич с WWDC 2020 закончен. Спасибо, что дочитали до конца.
Надеюсь, эта статья будет вам полезной. Happy testing!