Тестирование UWP приложений с помощью Appium

6d122f13899244c8aca443f6c31ade25.jpg

На данный момент UWP приложения используют в качестве решения для тестирования через интерфейс Coded UI. Но если у вас приложение под несколько платформ, то в этом случае вам придется писать тесты под каждую платформу отдельно.

В 2016-ом году была анонсирована возможность тестирования UWP приложений с помощью Appium. Для этого был создан драйвер под названием Windows Application Driver (WinAppDriver).

Selenium, использующий WebDriver API, был создан для автоматического тестирования веб приложений. Appium это фактически Selenium для Apps.

Тестировать с помощью Appium и WinAppDriver можно не только UWP, но и Win32 приложения (о .NET почему-то не упоминают, но можно тестировать и их). Мне же, как обычно, UWP приложения наиболее интересны.

Установка WinAppDriver


Скачиваем последний релиз со странички релизов на GitHub

После установки в директории C:\Program Files (x86)\Windows Application Driver будет расположен исполняемый файл WinAppDriver.exe, запустив который можно запустить сервис

fd24cdd9ae8c424884653be93c2a5d7a.PNG

Можно установить и Appium, который будет взаимодействовать с WinAppDriver, но WinAppDriver устанавливать нужно в любом случае. WinAppDriver должен устанавливаться вместе с Appium, но на момент написания статьи еще есть какие-то накладки, так что его приходится устанавливать отдельно. На всякий случай, давайте оставлю под спойлером как установить Appium на машину с Windows.

Как установить Appium на Windows 10 с помощью пакетного менеджера npm
Для работы с Appium необходим Node.js версии 6.0 или выше и npm версии 3.5 и выше
Проверить версию Node.js можно с помощью команды консоли
node –v
Версию npm
npm –v
Скачать установщик Node.js можно с официального сайта
При установке устанавливается и npm

b211acdd6c3b45f6acf1c08407994904.PNG

Если установленные версии выше чем минимально требуемые, то можно следующей командой запустить установку (командная строка должна быть запущена от имени администратора):
npm install –g appium
Теперь в командной строке можно запустить appium и получить следующее сообщение:

ce6a216320d04355b0f8d181056714b0.PNG


Создание проекта приложения для тестирования


Создадим простое UWP приложение, которое сразу же и протестируем. Пусть приложение совершает какую-нибудь простую операцию, например, возведение числа в квадрат. Исходный код демо приложения проще некуда.

В MainPage.xaml добавим:


       
Введите число:
 

Результат:
 


    

А MainPage.xaml.cs только одно событие:
private void btnGetSquare_Click(object sender, RoutedEventArgs e)
 {
     double n = Convert.ToDouble(txtNumber.Text);
     txtResult.Text = (n * n).ToString();
 }

Из манифеста нам необходимо сохранить Package family name. Оно необходимо для написания теста.

e21a79a6de024ab1b3cb1c7d0599a46d.PNG

Это же значение можно взять и из файла vs.appxrecipe, который находится в папке Debug/AppX. Значение содержится внутри RegisteredUserModeAppID.

Перед тем, как тестировать приложение, необходимо его развернуть.
Вот такое приложение получилось:

c14a9be8da7b40acac5fd0a175ab27d7.PNG

С помощью утилиты inspect.exe, которая расположена в директории C:\Program Files (x86)\Windows Kits\10\bin\x86 можно определить наименования элементов для использования в тесте.
Запустив приложение, которое необходимо протестировать, можно или выбрать в дереве любой интересующий контрол или даже просто кликнуть на контрол в окне самого приложения. Получим приблизительно такую информацию:

b088f746463f4bb3a87b6a8866685bcf.png

Для поиска элемента обычно используются следующие значения: ClassName и Name. С драйвером IOs можно использовать поиск элемента по его AutomationId.

Создание проекта автоматизированного тестирования


В Visual Studio создаем проект типа Unit Test

12ece22ec77d49489179e46a3fadb81b.PNG

В менеджере пакетов NuGet находим и устанавливаем Appium.WebDriver вместе с необходимыми для его работы зависимыми пакетами.

19062bd7cdd64919b8165b4efea7c1bf.PNG

Для работы с UWP приложением может быть использован RemoteWebDriver, а может быть использован IOSDriver. Скорее всего после окончательного релиза появится какой-то третий драйвер именно под платформу Windows. Windows Namespace должен быть включен в NuGet пакет Appium .NET Driver.
Пишем код теста. В данном случае он может быть таким:

  [TestClass]
    public class UnitTest1
    {
        protected const string AppDriverUrl = "http://127.0.0.1:4723";
        protected static RemoteWebDriver AppSession;

        [ClassInitialize]
        public static void Setup(TestContext context)
        {
            DesiredCapabilities cap = new DesiredCapabilities();
            cap.SetCapability("app", "6b86c2c7-c428-4039-9281-8da10ee45769_dyre41xy79knw!App");
            AppSession = new RemoteWebDriver(new Uri(AppDriverUrl), cap);
            Assert.IsNotNull(AppSession);
        }

        [ClassCleanup]
        public static void TestsCleanup()
        {
            AppSession.Dispose();
            AppSession = null;
        }

        [TestMethod]
        public void MakeOperation()
        {
            AppSession.FindElementByName("txtNumber").Clear();
            AppSession.FindElementByName("txtNumber").SendKeys("5");
            AppSession.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(1));

            AppSession.FindElementByName("btnGetSquare").Click();
            AppSession.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(1));

            RemoteWebElement txtResultTextElement;
            txtResultTextElement = AppSession.FindElementByName("txtResult") as RemoteWebElement;
  	     Assert.IsNotNull(txtResultTextElement);

            Assert.AreEqual("25", txtResultTextElement.Text);
            AppSession.Quit();
        }
    }

Если используется IOSDriver, то код инициализации изменяется на следующий:
protected const string AppDriverUrl = "http://127.0.0.1:4723";
protected static IOSDriver AppSession;

        [ClassInitialize]
        public static void Setup(TestContext context)
        {
            DesiredCapabilities cap = new DesiredCapabilities();
            cap.SetCapability("app", "6b86c2c7-c428-4039-9281-8da10ee45769_dyre41xy79knw!App");
            AppSession = new IOSDriver(new Uri(AppDriverUrl), cap);
            Assert.IsNotNull(AppSession);
        }

Как вы можете заметить изменения минимальные. Код теста MakeOperation остается прежним.

Дополнительно можно (а если запускаете Appium, то даже нужно) указывать платформу и устройство:

  cap.SetCapability("platformName", "Windows");
  cap.SetCapability("deviceName", "WindowsPC");

Если вы запустили WinAppDriver, то строка URL:
protected const string AppDriverUrl = "http://127.0.0.1:4723";

Ну, а если запустили Appium, то:
protected const string AppDriverUrl = "http://127.0.0.1:4723/wd/hub";

Запустим тест на выполнение. Можно таким образом:

02c81604c35c4845a798d2b57bb5304b.PNG

И в случае, если все верно, получим:

bdc6774e739649228c8dbbcfa02227f6.PNG

Небольшая анимация автоматизированного процесса:

3464c4eb599f425bbe5438af136ceccc.gif

Напоследок, приведу стандартный пример работы с Win32 приложением:

  DesiredCapabilities cap = new DesiredCapabilities();
  cap.SetCapability("app", @"C:\Windows\System32\notepad.exe");
  AppSession = new RemoteWebDriver(new Uri("http://127.0.0.1:4723"), cap);
  AppSession.FindElementByClassName("Edit").SendKeys("Привет Appium!");

Этот пример открывает блокнот и вводит текст. Для тестирования .NET приложения точно так же достаточно прописать путь к исполняемому файлу.

Пара ссылок:
Страничка на GitHub
Страничка официальной документации: Windows Application UI Testing

Комментарии (5)

  • 20 декабря 2016 в 11:37

    0

    Для чего нужно свойство AutomationProperties.Name? В коде везде, где нужно указан x: Name, в тестах тоже запросы идут по FindElementByName, так зачем это AutomationProperties.Name? В мсдн что-то невнятное про эти автосвойства написано, так и не осилил для чего они вообще нужны.
    Разжуйте, пожалуйста, что это, зачем и надо ли указывать?
  • 20 декабря 2016 в 11:47

    0

    Сейчас вы можете использовать или RemoteWebDriver или IOSDriver
    В данном случае, если вы используете IOSDriver, то можете искать элемент по его AutomationId:
    AppSession.FindElementByAccessibilityId("txtResult") as IOSElement;
    

    Когда выйдет специализированный Windows драйвер тоже скорее всего можно будет использовать поиск по AutomationProperties.Name
    • 20 декабря 2016 в 12:15

      0

      Какие-то преимущества от использования AutomationProperties.Name по сравнению с x: Name есть? Я так понимаю в данном контексте AutomationProperties.Name не нужен, правильно? А зачем он вообще?
      • 20 декабря 2016 в 12:27

        0

        Это всего лишь один из способов получить элемент. Да, в данном примере он не используется. Преимущество, на мой взгляд, в том, что разработчик приложения и разработчик тестов могут использовать свои идентификаторы, которые не взаимосвязаны.
      • 20 декабря 2016 в 15:08

        0

        К предыдущему комментарию добавлю что x: name в xaml+mvvm практически не используется, а если и используется, то разработчик вправе менять значение этого атрибута.

© Habrahabr.ru