Кирилл Мачухин
January 20, 2016
Рано или поздно в любом проекте нужно что-то автоматизировать - будь то web-приложение, мобильное, Android или iOS. К примеру, мы столкнулись со сложным для понимания workflow. В процессе написания, с заказчиком мы выяснили все требования и пожелания, поняли их и задокументировали. Однако, спустя пару-тройку недель очень сложно тестировать и вспоминать всякие тонкости. Поэтому, хорошо бы это автоматизировать.
Наш проект Hey Ya! - приложение на Android и iOS и web-сервер, работающий на Rails. Инструменты, которые предоставляет Интернет - это Calabash Android, MonkeyTalk, Robotium, Selendroid, UIAutomator для Android и Calabash iOS, Frank, UIAutomation, KeepItFunctional и ios-driver для iOS.
Какой же инструмент выбрать? Ведь все они хорошо работают, но не все подходят нашим критериям. А именно:
Сервер у нас на RoR.
- Необходима кроссплатформенность: у нас есть Android и iOS.
- Важно, чтобы framework регулярно обновлялся.
- И немаловажный фактор - запуск тестов на реальных устройствах, поскольку приложение основано на работе с камерой. А в iOS-симуляторе, как известно, работа с камерой невозможна.
Выбор пал на Calabash.
Почему? Плюсы инструмента:
- Он есть под Android-платформу, есть под iOS-платформу.
- Написан на Ruby.
- Имеет синтаксис Gherkin (Cucumber-style).
- Последнее обновление (на момент написания статьи) - месяц назад, т.е. обновляют его достаточно регулярно.
- Поддерживается запуск тестов на реальных устройствах.
Итак, приступим к установке.
Для начала необходим xcode. В нашем проекте устанавливаем gem и генерируем команду calabash-ios-setup. Команда calabash-ios-gen генерирует skeleton, который практически ничем не отличается от skeleton'a Cucumber, кроме того, что там свои launcher'ы. На скриншоте приведён launcher для iOS-приложения.
Команда calabash-ios-setup генерирует схему Calabash для iOS-проекта непосредственно в самом xcode, подключает Calabash framework к проекту. Выполняется всё просто - одной командой.
Для запуска тестов на реальном устройстве необходим bundle-kit нашего приложения, который можно легко достать из xcode и device end point. Device end point это ничто иное, как IP устройства, подключенного к Wi-Fi. Командой для запуска является просто Cucmber. Таким образом запускаются тесты. Переменную окружения нужно прописать в команде, а можно сразу указать её внутри launcher’а.
Android-установка практически такая же. Отличаются только launcher’ы в skeleton’e.
Запуск тестов на Android происходит командой calabash-android-run и указанием пути к нашему apk-файлу. На iOS нам необходимо заранее собрать (сбилдить) приложение на устройстве, чтобы мы смогли работать с ним. И собрать (сбилдить) именно со схемой Calabash, т.к. тестовый сервер устанавливается непосредственно в приложении.
Но вырисовывается такая ситуация, что у нас есть два проекта (iOS и Android). Возьмём какую-нибудь банальную feature вроде sign up. Всем известна структура Cucumber: у нас есть файл-feature, потом файл-steps и мы используем page_object_pattern, после чего происходит реализация функций, вызванных steps'ами. Выходит так, что до какой-то стадии получается одинаковый код: sign_up_features и sign_up_steps идентичны и в iOS, и в Android. Почему? Потому что дизайн наших приложений почти одинаковый. "Почти" потому, что некоторые вещи невозможно реализовать в Android, но можно в iOS и наоборот.
Что же нам делать, чтобы избежать дублирования? Как это обойти? За это решение отдельная благодарность Дмитрию Дордовскому и Андрею Кутейко, за то, что помогли найти и реализовать его.
Решение банальное и простое: установить gem'ы на стороне сервера, т.е. выделить наш Calabash из мобильных проектов, разбить наш page_object на модули и склеить launcher'ы calabash_iOS и calabash_Android.
И теперь если в переменном окружении мы указываем клиент-платформу iOS, то мы будем подключать Cucumber. Если клиентская платформа - Android при запуске наших тестов, значит мы будем идти по пути Calabash Android и отображать нужные файлы и методы.
Немаловажным фактором является здесь запуск тестового сервера. Это означает, что мы не ходим нашим приложением на staging/production. Мы ходим им на локальный сервер. Тесты лежат вместе с сервером, на серверной части. Мы имеем доступ к серверу и нам не составляет труда его поднять. Мы его поднимаем на host 0.0.0.0, и в приложении указываем host, по которому будем ходить - IP.
Разбиваем на модули. Папка features содержит page_object_pattern и она стала содержать в себе Android и iOS. Вот пример одного модуля pages general - там содержится код, который выполняет наши iOS-actions. В папке Android находится всё, что относится к Android: pages и все необходимые методы.
Что мы получили при таком подходе? У нас есть один файл signup_feature, а не два и один файл signup_steps_rb. На этом шаге мы не делаем никакого разделения, так как у нас одинаковые шаги для iOS и Android. После файла steps мы идём по пути iOS или Android, используя iOS- или Android-модули. Команды для запуска выглядят следующим образом: указывается платформа, запускается с помощью команд cucumber или calabash-android run и проверяется.
Плюсы такой реализации в том, что мы не имеем повторяющегося кода, в файлах feature и steps_rb - эдак в полтора раза мы сократили написание и дублирование. Появилась возможность свободного доступа к объектам через ActiveRecord и эмуляции работы второго клиента на стороне сервера. В частности, наше приложение не имеет такой функции, как logout и порой необходимо проверить кейсы, где первый клиент что-то сделал, а вторым надо проверить. Таким образом, на стороне сервера мы эмулируем работу и клиентом просто проверяем.
Естественно, есть и минусы такого подхода, хотя здесь они сугубо визуальны - это различные команды запуска (cucumber, calabash-android run). А так же нет возможности работать в debug режиме предоставляемым calabash (спасает команда binding.pry) В нужном месте остановились, проверили в debug режиме, проверили что нам надо, нашли элемент нужный и продолжили работу. Эти минусы есть, но они не критические.
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.