На данный момент 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.
Проверить версию 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)
asommer
20.12.2016 11:47Сейчас вы можете использовать или RemoteWebDriver или IOSDriver
В данном случае, если вы используете IOSDriver, то можете искать элемент по его AutomationId:
AppSession.FindElementByAccessibilityId("txtResult") as IOSElement;
Когда выйдет специализированный Windows драйвер тоже скорее всего можно будет использовать поиск по AutomationProperties.Name
dmitry_dvm
20.12.2016 12:15+1Какие-то преимущества от использования AutomationProperties.Name по сравнению с x:Name есть? Я так понимаю в данном контексте AutomationProperties.Name не нужен, правильно? А зачем он вообще?
asommer
20.12.2016 12:27Это всего лишь один из способов получить элемент. Да, в данном примере он не используется. Преимущество, на мой взгляд, в том, что разработчик приложения и разработчик тестов могут использовать свои идентификаторы, которые не взаимосвязаны.
SZolotov
20.12.2016 15:08К предыдущему комментарию добавлю что x:name в xaml+mvvm практически не используется, а если и используется, то разработчик вправе менять значение этого атрибута.
dmitry_dvm
20.12.2016 16:36По-моему опыту еще как используется в байндингах и бихевиорах. Чего не скажешь про AutomationProperties. Так и не понял для чего они.
SZolotov
20.12.2016 16:52+1что бы разделить тестирование и разработку, что бы разработчик не зависел от нужных тестировщику наименований и наоборот
lexxmark
22.12.2016 09:28Подскажите пожалуйста, как можно «расширять» Appium?
Например, у меня есть библиотека кастомных контролов под windows, могу ли я для этих контролов реализовать методы доступа к их кастомному состоянию и специфичные для них воздействия?
dmitry_dvm
Для чего нужно свойство AutomationProperties.Name? В коде везде, где нужно указан x:Name, в тестах тоже запросы идут по FindElementByName, так зачем это AutomationProperties.Name? В мсдн что-то невнятное про эти автосвойства написано, так и не осилил для чего они вообще нужны.
Разжуйте, пожалуйста, что это, зачем и надо ли указывать?