Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Всем привет! Меня зовут Стас, и я Senior QA в компании inDrive, наставник на курсе «Инженер по тестированию плюс» в Яндекс Практикуме. Ещё веду телеграм-канал «Тестировщики нужны».
Многие начинающие автоматизаторы задаются вопросом: «Как же написать свой первый автотест на мобильное приложение?». В данной статье я и хотел бы ответить на этот вопрос. На самом деле, материалов на эту тему написано большое количество. Подробно рассказывается применение паттерна PageObject в написании тестов и многое другое. Но наша с вами задача — написать наш первый простенький тест на экран авторизации на языке Java. Давайте решим эту задачу вместе!
Введение в тестирование мобильных приложений
На текущий момент при тестировании практически любого мобильного приложения вы столкнётесь с двумя операционными системами: iOS и Android. В данной статье мы будем работать с Android-приложением.
Для выбора правильного подхода к тестированию мобильного приложения необходимо определиться с его видом. На текущий момент имеется три вида приложений:
нативные приложения — такие приложения, которые устанавливаются на смартфон и работают непосредственно на смартфоне. Нативные приложения не предполагают работу и взаимодействие с браузером и интернетом;
веб-приложения — такие приложения адаптированы под десктопную и мобильную версию;
гибридные приложения — нативные приложения, внутри которых встроена возможность открывать веб-страницы.
Определившись с видом и типом мобильного приложения, можно смело переходить к разработке тестов. Обычно тестами называют последовательность шагов, выполнение которых приводит продукт в требуемое состояние. Во время тестирования исполняется весь набор заготовленных заранее тестов.
Существует два, скажем, вида тестирования — ручное и автоматизированное. В ручном тестировании тесты, разумеется, исполняет тестировщик, обычно такая позиция называется Manual QA. И тестировщик является связующим звеном между тестами и приложением. Выглядит это примерно следующим образом.
Сказать, что тестировщик напрямую работает с приложением, не совсем верно. Во время исполнения тестов он нажимает на кнопки, что-то вводит в поля, иными словами, работает с интерфейсом приложения. Также тестировщик работает и с API сервиса, и с сетевым интерфейсом, CLI, GUI и т. д. Если API используются клиентами-программами для взаимодействия с сервером, то GUI используется человеком.
Ручное тестирование — это, конечно, хорошо, но давайте посмотрим на вариант замены ручного труда на автоматизированный. Вместо тестировщика с приложением будут взаимодействовать программные клиенты. Но кто же будет запускать тесты, спросите вы? Верно! За запуск тестов будет отвечать фреймворк тестирования. Он запускает тесты, показывает результаты прогонов, хранит историю запусков и многое другое.
Для работы с GUI интерфейса необходимы специальные адаптеры, их ещё называют драйверами (да, да, многие вспомнят, чтобы звук на компьютере воспроизводился, а, например, видеокарта передавала изображение, необходимо было устанавливать драйверы). Например, в нашем тесте имеется шаг с тапом на кнопку Поиск. Драйвер получает команду, что ему нужно тапнуть на кнопку Поиск, а затем эту команду передаёт нужному интерфейсу в нашем приложении. В результате нажимается кнопка Поиск, тест успешно пройден. Если же кнопки нет либо по какой-то причине она не нажимается, то будет ошибка, тест падает.
Часто функциональности драйвера может не хватать для решения каких-либо нестандартных задач, не хватает должного уровня логирования. И для решения таких проблем используются различные обёртки. Например, Kakao или Kaspresso. Эти обёртки могут сильно различаться в зависимости от решаемых задач. Некоторые из них — просто синтаксический сахар (это синтаксические возможности, применение которых не влияет на поведение программы, но делает использование языка более удобным для человека), другие же — довольно толстая прослойка, выполняющая много сложной работы.
Установка инструментов
Выбор языка
Существуют различные языки, которые можно использовать для автоматизации тестирования, например: Java, Python, Kotlin, Swift, JavaScript и многие другие. Всё зависит от вас и от того, какие цели вы преследуете. Мы будем использовать в нашем примере язык Java. Для этого необходимо его скачать и установить с официального сайта.
Android Studio
Android-приложение можно запустить как на физическом устройстве, так и на эмуляторе. Чаще всего запускают автотесты на эмуляторах. Для этого необходимо скачать и установить Android Studio с официального сайта.
Appium
Следующий наш инструмент — Appium (инструмент с открытым исходным кодом, который помогает автоматизировать приложения как для Android, так и для iOS). Есть две возможности запуска Appium: UI или консольная. Для установки и запуска консольной версии нам дополнительно понадобится NodeJs. А UI-версию можно скачать с официального сайта, и она сразу будет готова к работе.
Appium Inspector
Для поиска локаторов (локатор — это путь к элементу в интерфейсе, с помощью которого автоматизированный тест (автотест) сможет его найти) нам понадобится инспектор графического интерфейса.
Appium Inspector — это просто клиент Appium с пользовательским интерфейсом, где отображается экран тестируемого приложения и где мы можем посмотреть пути к элементам.
IntelliJ IDEA
Разрабатывать мы будем в IntelliJ IDEA, которая также доступна для скачивания с официального сайта, где я предлагаю выбрать Community Edition версию.
Приложение
Мы с вами будем разрабатывать тесты на приложение, которое вы сможете скачать себе и самостоятельно собрать в Android Studio. Оно доступно по ссылке. Или же можно скачать apk.
Настройка и установка эмулятора Android Studio
Вопрос выбора операционной системы для автоматизации Android не сильно критичен. У вас может стоять как ОС Windows, Linux, так и MacOS.
Для старта нам будет нужен Android SDK и установленная Android Studio. Нам необходимо будет создать эмулятор. Не будем в статье останавливаться на этом, так как гайд по настройке эмулятора хорошо расписан в официальной документации.
Настройка Appium
После того как мы настроили Android Studio, давайте мы с вами настроим Appium. Откроем установленный ранее Appium UI. Нам необходимо в поле Host указать адрес 0.0.0.0, а в Port: 4723.
Затем перейдём в Edit Configurations и укажем пути ANDROID_HOME и JAVA_HOME, как показано на скриншоте ниже, и нажмём Save and Restart:
Обратите внимание, что ваши пути могут отличаться от представленных на скриншоте, как минимум, разными пользователями.
Затем перейдём в Appium во вкладку Advanced и проверим, что ваши настройки в полях Server Address, Server Port такие же, как и на скриншоте ниже:
После настройки всех необходимых полей нажмём кнопку Start Server и убедимся, что Appium Server запущен:
На данный момент нам с ним делать ничего не нужно, оставим запущенный Appium Server.
Настройка Appium Inspector и поиск локаторов
Для того чтобы наши тесты понимали, с какими элементами интерфейса мы будем взаимодействовать, нам необходимо узнать, как к этим элементам можно обращаться. Для этого запустим Appium Inspector и укажем Remote Host: 0.0.0.0, а также Remote Path /wd/hub, как показано ниже на скриншоте:
Теперь перейдём к Capability. Capability — это набор параметров и значений, используемых для запуска сеанса Appium. Информация в наборе используется для описания того, какие «возможности» вам требуются во время запуска эмулятора. Например, определённую мобильную операционную систему или определённую версию устройства. Capability представлены в виде пар ключ-значение, причём значения могут быть любого допустимого типа JSON.
{
"appium:platformName": "Android",
"appium:deviceName": "autoschool10",
"appium:app": "/Users/s.yakovlev/Desktop/login.apk"
}
В переменной appium:platformName
у нас указана платформа Android, в переменной appium:deviceName мы указываем имя нашего созданного Android эмулятора, у нас он называется "autoschool10", а также путь, до .apk.
Сохраним указанные capability и назовём этот пресет как loginAndroidApp, на самом деле название не играет никакой роли, оно должно быть понятным вам самим.
Сохранённые capability доступны во вкладке Saved Capability:
Теперь нам осталось открыть Android Studio и запустить созданный ранее эмулятор и в Appium Inspector нажать Start Session.
У нас запускается Appium Inspector, где в левой части отображается снимок экрана, а справа дерево элементов.
Мы напишем три теста:
первый будет проверять, что отображается название Login в таббаре,
второй тест проверит позитивный сценарий авторизации,
а третий тест проверит негативный сценарий авторизации.
Для этого нам понадобятся пути к данным элементам. Начнём с таббара. Кликнем на название Login — и мы увидим, что справа отобразятся свойства данного элемента:
Выпишем id этого элемента, он нам понадобится несколько позже: com.example.login:id/toolbar
То же самое проделаем с полями Email и Password. Для Email id будет com.example.login:id/username
и com.example.login:id/password
соответственно для поля с паролем.
Также узнаем id для кнопки SIGN IN OR REGISTER:
Её id будет com.example.login:id/login.
Если пользователь вводит следующие валидные данные email: admin@admin.ru и пароль: 1234, то ему показывается текст: Welcome ! user. Введём данные в поля и убедимся:
Но, как вы можете заметить, в инспекторе ничего не поменялось, а нам нужно узнать id этого текстового поля. Ничего страшного, давайте просто нажмём на кнопку Refresh
и сделаем новый снимок экрана. К нам подтянулся новый снимок экрана, и мы можем теперь узнать id этого текстового поля com.example.login:id/succestext:
Если же ввести в эмуляторе неверный пароль или почту, то будет ошибка авторизации и покажется соответствующий текст.
Вы, конечно, могли уже заметить, что данное сообщение об ошибке имеет точно такой же id, но если приглядеться, то текст ошибки в поле text совершенно иной. Это нам и потребуется.
Теперь, когда все id элементов мы выписали, давайте приступим к написанию тестов.
Настройка библиотек
Разрабатывать тесты мы будем в IntelliJ IDEA. Для этого запустим IDEA и создадим директорию, например, на рабочем столе и назовём её androidAutomation.
После того как мы создали дефолтный проект, давайте подключим к нему библиотеки:
Java Client 7.1.0:
Selenium server standalone:
JUnit 4:
Для вашего удобства мы собрали все библиотеки в одном месте.
Откроем созданный проект и создадим директорию libs:
Далее поместим в нашу директорию скачанные библиотеки:
Теперь перейдём в наш проект, в раздел Project Structure, и добавим наши библиотеки из папки libs:
Мы добавили все необходимые для работы библиотеки, осталось создать директорию, куда мы поместим наше приложение. Создадим директорию apks и перетащим в неё наше приложение:
Разработка тестов
Теперь перейдём в директорию main — java и создадим наш первый класс, который назовём LoginTest, в нём мы и будем писать наши тесты:
Для успешного запуска теста нам понадобятся методы setUp(), tearDown() и сам тест. Напишем метод setUp(), в котором пропишем все необходимые параметры для запуска, а именно:
platformName — версия ОС нашего эмулятора, в нашем случае это Android;
deviceName — имя эмулятора, которое отображается в Android Studio, в нашем случае это autoschool10 (название эмулятора может быть любым, но лучше не давать ему имя, написанное кириллицей);
platformVersion — это версия нашей ОС — Android 10;
automationName — автоматизируем с помощью Appium;
appPackage — пакет нашего приложения тестового com.example.login;
appActivity — та активити, то есть тот экран, который будет запускаться. В нашем приложении всего лишь один экран, и эта активность называется .ui.login.LoginActivity;
app — тут указываем полный путь до нашего приложения, в данном случае это "/Users/{имя_пользователя}/Desktop/androidAutomation/apks/login.apk".
В данной строке мы подключаем android driver (Appium Android Driver — инструмент автоматизации тестирования для устройств Android. Драйвер является частью инструмента автоматизации тестирования Appium mobile):
driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
Система работает с помощью com.android.uiautomator.testrunner.UiAutomatorTestCase, размещённого на устройстве Android, который открывает SocketServer на порту 4723. Этот сервер получает команды, преобразует их в соответствующие команды Android UI Automator и запускает их на устройства.
Полный листинг кода представлен ниже:
public void setUp() throws Exception
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("deviceName", "autoschool10");
capabilities.setCapability("platformVersion", "10");
capabilities.setCapability("automationName", "Appium");
capabilities.setCapability("appPackage", "com.example.login");
capabilities.setCapability("appActivity", ".ui.login.LoginActivity");
capabilities.setCapability("app", "/Users/s.yakovlev/Desktop/androidAutomation/apks/login.apk");
driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
}
Завершим работу драйвера в методе tearDown():
public void tearDown()
{
driver.quit();
}
Затем добавим аннотацию @before
— то, что выполняется перед тестом, @After
— то, что выполняется после теста, @Test
— добавляется у каждого теста.
Всё, что нам остаётся, — это написать сами тесты. Мы напишем всего три теста. Первый будет проверять, что действительно у нас открылся экран Login. Второй будет проверять авторизацию с валидными данными, ну а третий, что логично, — авторизацию с невалидными данными.
Тест на проверку наличия Таббара
Напишем позитивный тест на проверку названия экрана. Мы объявим переменную, назовём её screenTitle, в которой сохраним наш элемент. Искать данный элемент мы будем по id, поэтому напишем findElementById. Чтобы убедиться, что элемент действительно есть, вызовем метод isDisplayed().
@Test
public void checkTitleScreen()
{
WebElement screenTitle = driver.findElementById("com.example.login:id/toolbar");
screenTitle.isDisplayed();
}
Тест на авторизацию с валидными данными
Перейдём к написанию теста на авторизацию с валидными данными. Последовательность действий будет ровно такой же. Задаём переменные emailInput и passInput, в которых сохраняем наши ранее найденные элементы.
Чтобы заполнить поле данными, по нему надо сначала произвести клик, для этого существует метод click(), а для заполнения данных мы будем использовать метод sendKeys(), в который уже и передаём те данные, которыми мы хотим заполнить наше поле.
@Test
public void validRegistrationTest()
{
WebElement emailInput = driver.findElementById("com.example.login:id/username");
emailInput.isDisplayed();
emailInput.click();
emailInput.sendKeys("admin@admin.ru");
WebElement passInput = driver.findElementById("com.example.login:id/password");
passInput.isDisplayed();
passInput.click();
passInput.sendKeys("1234");
WebElement signInButton = driver.findElementById("com.example.login:id/login");
signInButton.isDisplayed();
signInButton.click();
WebElement successAuthText = driver.findElementByXPath("//*[contains(@text, 'Welcome ! user')]");
successAuthText.isDisplayed();
}
Обратите внимание, что мы искали элементы по id, но текст “Welcome ! user” мы ищем по XPath. Это второй вариант поиска локаторов. XPath используется для навигации по элементам и атрибутам. Следующей конструкцией findElementByXPath("//[contains@textt, 'Welcome ! user')]"); мы ищем текст по наличию на всём экране. Методу contains() не требуется весь текст для правильной идентификации элемента, достаточно части текста, но важно, чтобы эта часть текста была уникальной.
Тест на авторизацию с невалидными данными
Аналогичным образом напишем тест на авторизацию с невалидными данными:
@Test
public void validRegistrationTest()
{
WebElement emailInput = driver.findElementById("com.example.login:id/username");
emailInput.isDisplayed();
emailInput.click();
emailInput.sendKeys("user@user.ru");
WebElement passInput = driver.findElementById("com.example.login:id/password");
passInput.isDisplayed();
passInput.click();
passInput.sendKeys("1111");
WebElement signInButton = driver.findElementById("com.example.login:id/login");
signInButton.isDisplayed();
signInButton.click();
WebElement successAuthText = driver.findElementByXPath("//*[contains(@text, 'Login failed')]");
successAuthText.isDisplayed();
}
Наши тесты написаны и готовы к запуску. Для запуска всех тестов нажмём на зелёный треугольник около класса LoginTest:
Важно: перед запуском убедитесь, что у вас запущен Appium Server GUI и запущен сам эмулятор.
Наши тесты запущены, и мы можем наблюдать на эмуляторе процесс прогона — поля заполняются данными; если тест проходит успешно, то он отмечается зелёной галочкой.
После прогона мы можем посмотреть дефолтный отчёт. Он, конечно, не информативный, но всё равно мы можем увидеть, что наши тесты успешно пройдены.
Вывод
Мы с вами рассмотрели, как пишутся тесты на языке Java на мобильное приложение под ОС Android. Конечно, данные тесты далеко не идеальны. На проектах обычно пишут целые фреймворки и используют паттерн PageObject. Но наша с вами задача была — написать свой первый тест, а начать можно и без паттерна PageObject.
Полный листинг проекта с тестами.
Проект с Android-приложением.