В предыдущей статье мы говорили о inout, ленивых, а также о геттерах и сеттерах.



В этой статье мы сосредоточимся на написании тестов. Вы можете подумать, что это необязательно, потому что он не выполняет никакой логики, которая будет поставляться с вашим приложением. Если вы так думаете, вы проиграете собеседование.

Тестирование

Тестирование важно, и всякий раз, когда я его слышу, приходит на ум какой-то термин. Разработка через тестирование (TDD). Разработка через тестирование - популярный способ написания приложений, и формулу легко запомнить.

  1. Напишите неудачный тестовый пример.
  2. Напишите достаточно кода и не более того, чтобы пройти тест.
  3. Протестируйте, чтобы убедиться, что дело прошло успешно.

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

В этой статье я буду рассматривать только TDD. Прежде чем мы начнем, я хотел бы выделить два новых термина. Коэффициент тестирования и Покрытие кода.

  • Коэффициент тестирования - отношение строк кода к количеству написанных тестов. Многие люди скажут, что 1 строку кода следует протестировать с помощью 3 тестов, записанных как 1: 3.
  • Покрытие кода - включает в себя процентную долю протестированного кода. Многие IDE предлагают представление покрытия кода, которое выделяет код в зависимости от статуса тестирования. Зеленый означает, что есть тест, красный - нет.

Хотя я должен по умолчанию «тестировать все», как и все остальные, я этого не сделаю. Вам не нужно впадать в крайности при тестировании функций, включенных в Swift или другие библиотеки, которые включают тесты. Я бы не стал писать тестовый пример для оператора печати или другого статического метода, такого как Date.init(). На самом деле вам нужно только протестировать свой код, за исключением чужого кода, который не включает тесты.

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

Давайте пройдемся по этой строке за строкой. import XCTest импортирует библиотеку, предоставленную Apple для тестирования. @testable import ViewController @testable - это атрибут, который увеличивает область доступа для модуля. Это существенно изменяет уровень доступа с internal или private на open, но только для ваших локальных тестов. import ViewController - это оператор импорта, который включает класс, который вы хотите протестировать.

class ViewControllerTests: XCTestCase { ... } определяет класс, который наследует все функции от XCTestCase. Если вы используете какие-либо свойства, будь то другой класс, или, возможно, некоторые константы или переменные, которые будут часто использоваться в ваших тестовых примерах, замените // Testing properties here этими свойствами.

override func setUp(). Думайте об этом как о viewDidLoad() для ваших тестовых случаев. Когда вы начинаете тестирование, это вызывается для создания классов или создания экземпляров переменных.

override func tearDown(). Это эквивалент deinit {} в ваших контроллерах представления. Удалите здесь все, что может вызвать утечку памяти, Timer - хороший пример того, что следует удалить.

func test_multiplyByTwoReturnsFour(). Это одно из многих соглашений об именах, но оно дает представление о том, для чего используется этот тест. Всегда начинайте свои тестовые примеры с test _ не является обязательным, но помогает улучшить читаемость. Тогда multiplyByTwo это то, что мы делаем, ReturnsFour это то, что мы ожидаем. Если вы напишете свои кейсы таким образом, вы всегда будете знать, для чего используется каждый тестовый пример и каковы ожидаемые результаты. Что, если мы вернем только нечетные числа и округлим в конце? Мы могли бы написать тестовый пример, например
func test_getOddFromMultiplyByTwo_ReturnsFive().

Наконец, у нас есть XCTAssertEqual(value, value). Это реальный тестовый пример, который проверяет, равны ли оба значения. XCTAssert - распространенный префикс, поэтому, если вы начнете набирать XCT, автозаполнение в XCode предоставит вам множество методов, которые вы можете использовать. В этом случае, если оба значения равны, тест пройден, в противном случае - нет.

Некоторые другие распространенные тесты: XCTAssertNotEqual, XCTAssertNil и XCTAssertNotNil.

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

В отличие от наших тестов утверждения, нам не нужно включать раздел ожидаемого возврата в объявление нашего метода. Вместо этого мы просто заменим это на
test_functionNamePerformance(). Блок measure { } должен содержать только код, который мы хотим измерить. Если бы мы передавали переменную в getFactorial(of:), мы бы создали ее экземпляр вне блока меры, если только мы не захотим включить ее в наши измерения.

_ = getFactorial(of: 45) просто вызывает метод getFactorial с вводом 45. Поскольку нам не нужно сохранять значение, мы просто используем _ =, что похоже на перемещение результата в /dev/null. Нам все равно, какова ценность, здесь это не важно.

Что нас действительно волнует, так это то, сколько времени потребуется факториальной функции, чтобы выполнить свою работу. Когда вы запустите это, тестовый пример запускается 10 раз.

Вот как выглядит наша getFactorial(of:) функция для справки.

При первом запуске это выполнило свою функцию за 0,00000263 с. Это довольно быстро, но за это время производилась оптимизация, давайте установим базовый уровень и запустим его снова.

Во втором прогоне мы выполнили это на 39% лучше, но ничего не изменилось. Это потому, что оптимизации уже есть, и мы просто использовали их повторно. Это хорошее место для установки нашей новой базовой линии, поэтому мы нажимаем «Изменить», затем принимаем новую базовую линию и сохраняем ее. При последующих запусках я обнаружил варианты на 6% хуже, на 0% лучше, на 2% лучше и на 7% лучше без изменения строчки кода. Для нас это хорошая база. Отсюда мы могли бы внести изменения, а затем повторно протестировать наши измерения, чтобы увидеть, улучшили мы ситуацию или ухудшили.

Несмотря на то, что не существует строгих рекомендаций относительно того, что считается хорошей оптимизацией, я считаю, что оставлять любые изменения, которые неизменно лучше после 10 измерений (100 прогонов). Если в первые несколько запусков все будет значительно лучше, я сохраню обновленный код.

Когда дело доходит до тестирования, модульное тестирование - это то, о чем мы говорили выше. Существует также общее тестирование, которое проверяет общую функциональность вашего приложения, работающую должным образом. Тестирование пользовательского интерфейса - это еще один тип тестирования, охватывающий ваш пользовательский интерфейс, но, поскольку это руководство касается только основ, оно выходит за рамки данной статьи. Наконец, существует приемочное тестирование пользователей (UAT), которое гарантирует, что пользователям понравится то, что у них есть. Обычно это делают группы QA и бизнес или избранная аудитория конечных пользователей (читайте о TestFlight). Это позволяет вам отправлять в приложение больше сценариев для дальнейшего тестирования и не задавать вопросы «Как мне _________?» когда приложение будет запущено.

Резюме

Мы узнали основы тестирования и то, как оно может помочь вам писать лучший код, важность его в цикле разработки и способы измерения производительности вашего собственного кода. Дальнейшее тестирование включает использование инструментов Xcode, где вы можете измерить использование памяти, ЦП и диска (ввода-вывода), но это более продвинуто.

Убедитесь, что вы написали хорошие тесты для своего приложения, чтобы всегда знать, что оно будет работать должным образом.

Что дальше

Что дальше - архитектура Model View Controller.

Контроллер представления модели (MVC) широко используется для обучения новых разработчиков программированию. Это самый простой способ научиться использовать несколько файлов в проекте. Хотя есть и другие, из которых вы можете выбрать, они могут немного запутать вас. Когда вы будете готовы перейти от MVC, вы почти естественно начнете писать в другом архитектурном стиле в зависимости от потребностей вашего приложения.

А пока продолжайте практиковаться!