На данный момент 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, запустив который можно запустить сервис



Можно установить и 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



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



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


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

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

<StackPanel Orientation="Vertical" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
       
<TextBlock Margin="0,25,0,0" TextAlignment="Center">Введите число:</TextBlock>
<TextBox AutomationProperties.Name="txtNumber" x:Name="txtNumber" 
     Margin="0,5,0,0" FontSize="18" Width="150"> </TextBox>

<TextBlock Margin="0,25,0,0" TextAlignment="Center">Результат:</TextBlock>
<TextBox AutomationProperties.Name="txtResult" x:Name="txtResult" 
     TextAlignment="Center" FontSize="18" Width="150"> </TextBox>

<Button AutomationProperties.Name="btnGetSquare" x:Name="btnGetSquare" 
     Margin="0,20,0,0" HorizontalAlignment="Center" Click="btnGetSquare_Click">
Возвести в квадрат 
</Button>
    </StackPanel>

А 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. Оно необходимо для написания теста.



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

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



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

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



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

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


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



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



Для работы с 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<IOSElement> 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<IOSElement>(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";

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



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



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



Напоследок, приведу стандартный пример работы с 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
Поделиться с друзьями
-->

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


  1. dmitry_dvm
    20.12.2016 11:37

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


  1. asommer
    20.12.2016 11:47

    Сейчас вы можете использовать или RemoteWebDriver или IOSDriver
    В данном случае, если вы используете IOSDriver, то можете искать элемент по его AutomationId:

    AppSession.FindElementByAccessibilityId("txtResult") as IOSElement;
    

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


    1. dmitry_dvm
      20.12.2016 12:15
      +1

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


      1. asommer
        20.12.2016 12:27

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


      1. SZolotov
        20.12.2016 15:08

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


        1. dmitry_dvm
          20.12.2016 16:36

          По-моему опыту еще как используется в байндингах и бихевиорах. Чего не скажешь про AutomationProperties. Так и не понял для чего они.


          1. SZolotov
            20.12.2016 16:52
            +1

            что бы разделить тестирование и разработку, что бы разработчик не зависел от нужных тестировщику наименований и наоборот


  1. lexxmark
    22.12.2016 09:28

    Подскажите пожалуйста, как можно «расширять» Appium?
    Например, у меня есть библиотека кастомных контролов под windows, могу ли я для этих контролов реализовать методы доступа к их кастомному состоянию и специфичные для них воздействия?


  1. Arpa
    24.12.2016 19:15

    Добрый день, решил попробовать Appium, но столкнулся с проблемой.
    Подскажите, пожалуйста, как можно отправить Enter в TextBox?
    Заранее спасибо.


    1. asommer
      25.12.2016 10:00

      Здравствуйте.
      Попробуйте:

      driver.KeyEvent("13");