Pull to refresh

Автоматизация тестирования Windows-приложений с использованием Winium

Reading time5 min
Views19K

С чем едят


С помощью Winium мы можем автоматизировать обычные Windows-приложения. Как правило, Winium может работать с теми элементами, которые можно отыскать в окнах стандартными Windows-средствами (как правило, эти элементы имеют tab-ордер). Средства эти поставляются в стандартных китах (скачать, например, можно тут, после установки искать их, например, здесь: C:\Program Files (x86)\Windows Kits\8.1\bin\x64). Наиболее удобными для себя я считаю inspect и uiverify, но на вкус и цвет, как говорят некоторые мои товарищи, все фломастеры разные.

Однако, для того, чтобы автоматизировать самописные приложения, неплохо было бы товарищам разработчикам следить за тем, чтобы у элементов были адекватные ClassName, Name и AutomationId. Конечно, в определённых ситуациях нас могут спасти передачи нажатий определённых клавиш и щёлканье мышкой по координатам, но это не панацея. Лучше всегда иметь набор заранее заготовленных «рычагов» в виде описаний объектов, чем полагаться на то, что GUI всегда будет неизменным и будет себя хорошо вести в различных ситуациях.

С Winium в нормальном режиме можно общаться посредством Selenium с использованием Python и Java; так или иначе, но звёзды встали так, что выбор пал на Java.

Установка


Во-первых, нам потребуется Winium Desktop Driver. Скачать его можно, например, тут. От нас потребуется это дело разархивировать и запускать при тестах. Через него Selenium сервер будет общаться с Windows-приложениями.

Во-вторых, собственно, Java-IDE (использовалась IntelliJ IDEA Community, за что им отдельное большое спасибо) и JDK. Не забываем проверить/прописать переменную среды JAVA_HOME со значением пути до JDK (в моём случае — C:\Program Files\Java\jdk1.8.0_131).

В-третьих, нужен нам и Selenium сервер — идем сюда, качаем Selenium Standalone Server (JAR-файл).

Далее по настройке проекта — создаём новый Java-проект и добавляем в External libraries (кликаем по кнопке Project Structure (или Ctrl+Alt+Shift+S), там в Project Settings выбираем Libraries и нажимаем плюсик) наш селениумный JAR-файл. На этом всё, можно писать.

Использование


Для того, чтобы всё работало, нужно на время использования тестов запускать Winium.Desktop.Driver с админскими правами. Но, так как драйвер время от времени может подвисать по тем или иным причинам, удобно его запускать перед каждым тестом и в конце убивать. Например, для этой цели можно использовать ProcessBuilder:

ProcessBuilder pro = new ProcessBuilder(windriverPath + windriverName, windriverParam);
shell = pro.start();
//<наш код>
shell.destroy();

где для удобства развёртывания можно задавать windriverPath, windriverName – путь до драйвера и имя исполняемого файла; windriverParam – дополнительные параметры запуска, ежели таковые потребуются.

В начале начал в коде нужно прикрепить переменную драйвера к собственно Winium драйверу:

DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability("app","<path to executable file>"); //если хотим сразу запускать какую-либо программу
cap.setCapability("launchDelay","5"); //задержка после запуска программы
WebDriver driver = new RemoteWebDriver(new URL("http://localhost:9999"),cap); //на этом порту по умолчанию висит Winium драйвер

Если мы не хотим запускать какое-то приложение, а просто прикрепляться к какому-либо уже запущенному, то 2 и 3 строчки можно смело опустить.

Для того, чтобы нам работать с каким-либо элементом, нам сначала нужно к нему прикрепиться. Известны три механизма прикрепления – By.name – по полю Name, By.className — по полю ClassName и By.xpath для более изощрённых условий поиска. Также если нам нужен первый/единственный элемент, мы можем использовать метод findElement, а если мы хотим получить список всех таких элементов, то нам нужно использовать метод findElements. Во втором случае элементы будут добавлены в порядке tab-ордера. Примеры использования:


WebElement wrk = driver.findElement(By.name("Значение поля Name")); //один элемент, поиск по полю Name
List<WebElement> wrkL = driver.findElements(By.className("Значение поля ClassName")); //список элементов с заданным полем ClassName

Так же мы можем прикрепляться к элементам другого элемента:


WebElement wrk1 = wrk.findElement(By.name("Значение поля Name"));

Если первые два механизма прикрепления очень узкоспециализированы, т.е. работают строго в иерархической структуре и строго с полями Name и ClassName, то для работы с иными случаями нам потребуется третий механизм, а именно By.xpath.

В этом механизме всё строго по канонам использования xpath (во всяком случае, с использованных случаях всё работало как нужно). С помощью xpath можно получить и обрабатывать поля IsOffscreen, AutomationId, HasKeyboardFocus:


WebElement field = wrk.findElement(By.xpath("*[@HasKeyboardFocus='True']")); //найдёт элемент у wrk, на котором стоит фокус

При использовании xpath, особенно если мы работаем в более сложных условиях, удобно для отслеживания вероятных ошибок логировать всю строку, передаваемую в xpath:


String xpathStr = "*[(@AutomationId='" + autId + "') and (@IsOffscreen='False')]"; //будем искать элемент у текущего окна с каким-то заданным AutomationId = autId и у которого свойство IsOffscreen = False 
log("Performing xpath search: " + xpathStr);
WebElement tWrk = wrk1.findElement(By.xpath(xpathStr));

Конечно же, мы можем искать xpath'ом элементы не только текущего окна, но такой поиск может длиться достаточно продолжительное время.

Есть еще другие способы поиска, но, так или иначе, все сводятся к описанным выше. Например, такие.

Также нам может потребоваться имитация ввода клавиш с клавиатуры. В обычном порядке это делается методом sendKeys(«Последовательность символов») у элемента. Однако, следует помнить, что некоторые символы используются как служебные и их надо экранировать (например, "+" это Shift, и для того, чтобы ввести "+", нужно передать последовательность "+="). Для удобства пользования кодом можно написать обёртку, которая бы автоматически заменяла все "+" на "+=", но тут как кому удобнее. Подробнее ознакомиться со стандартами передач комбинаций клавиш можно, например, тут. Тем не менее, возникали проблемы с корректной передачей нажатий стрелок на клавиатуре, так что к сожалению, при текущей версии драйвера придётся искать обходные пути.

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


Actions builder = new Actions(driver);
Action enter = builder
        .moveToElement(wrk)
        .moveByOffset(x,y)
        .click()
        .build();
enter.perform();

где wrk – имя WebElement, от центра которого будем двигать мышкой; x, y – расстояние, на которое будем двигать (положительное значение x двигает курсор вправо, положительное y – вниз).

Совсем чуть-чуть об общей структуре проекта


Как уже было слегка отмечено выше, основным элементом взаимодействия с внешним миром являются «рычаги». Их можно удобно описать классом с полями name, className и automationId и хранить в отдельных классах по блокам.

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

Всё остальное можно смело делать как обычно — классы с тестами, класс для запуска тестов и прочее.

Заключение


В целом погружение в мир Winium довольно интересно и не сильно сложно. Спасибо всем читателям, которые дочитали этот пост до конца. Очень надеюсь, что этот пост поможет кому-нибудь в освоении Winium. Отдельное спасибо моей напарнице Евгении, вместе с которой мы погружались в его дебри. Всем добра!
Tags:
Hubs:
+15
Comments8

Articles