[Из песочницы] Xamarin.UITest
В этой статье я расскажу вам о Xamarin.UITest — фреймворке для приемочного UI тестирования от Xamarin.
Что это такое и как его готовить
Xamarin.UITest сделан на основе Calabash и имеет сходный с ним принцип работы. Однако, тесты на данном фреймворке пишутся на С# и NUnit (в отличие от Calabash, в котором используется Gherkin+Ruby). Фреймворк кроссплатформенный и, следовательно, он позволяет писать тесты как для платформы iOS, так и для Android. Windows Phone не поддерживается, что я лично считаю недостатком данного инструмента. Зато в нем вполне поддерживается тестирование нативных приложений на Java и Objective-C.
Почему именно Xamarin.UITest
Конечно, многие из вас могут возразить, мол, для Java и Objective-C существуют свои нативные фреймворки, которые прекрасно справляются с возложенной на них задачей. Но если вам необходимо написать приемочные UI тесты одновременно для двух платформ, Xamarin.UITest придется как нельзя кстати. Более того, в нем есть изкоробочная поддержка Xamarin Test Cloud. Сам тестклауд — штука весьма полезная, но довольно дорогая.
Подробнее о том, как это работает
Механизм работы на iOS и Android отличатется. На iOS в приложение встраивается Nuget пакет, который поднимает http сервер внутри вашего приложения. Вот как это работает на iOS:
При работе с Android дела обстоят немного иначе. По сути, при сборке и запуске тестов на устройство устанавливаются два приложения: ваше и Test Cloud server. Оба они подписываются одним и тем же ключом, поэтому Test Cloud server имеет права на управление вашим приложением. Выглядит это следующим образом:
Встраивается Xаmarin.UITest в солюшн довольно просто, практически за пару кликов. Всего-то и нужно, что создать еще один проект для UI тестов.
После этих нехитрых действий добавляется проект с шаблоном инициализации и установленными пакетами. Затем, в iOS проект необходимо добавить Nuget пакет Xamarin Test Cloud Agent NuGet, а в AppDelegate мы прописываем следующие строки:
#if DEBUG
Xamarin.Calabash.Start();
#endif
return true;
Если вы используете замариновскую IDE, то на этом процесс интеграции можно считать завершенным. Для Visual studio придется все делать вручную, но и тут все довольно просто. Помимо манипуляций с iOS проектом, нужно добавить PCL проект и установить в него пакеты Nunit и Xamarin.UITest.
Теперь поговорим о конфигурировании наших тестов
Нас будут интересовать два класса: Tests и Appinitializer. Начнем со второго:
public class AppInitializer
{
public static IApp StartApp (Platform platform)
{
// TODO: If the iOS or Android app being tested is included in the solution
// then open the Unit Tests window, right click Test Apps, select Add App Project
// and select the app projects that should be tested.
if (platform == Platform.Android) {
return ConfigureApp
.Android
// TODO: Update this path to point to your Android app and uncomment the
// code if the app is not included in the solution.
//.ApkFile ("../../../Droid/bin/Debug/xamarinforms.apk")
.StartApp ();
}
return ConfigureApp
.iOS
// TODO: Update this path to point to your iOS app and uncomment the
// code if the app is not included in the solution.
//.AppBundle ("../../../iOS/bin/iPhoneSimulator/Debug/XamarinForms.iOS.app")
.StartApp ();
}
}
Кроме указанного в TODO пути к файлам apk и ipa, еще есть целый ряд параметров, например, идентификатор устройства, на котором будут запускаться тесты, api ключ для Xamarin Test Cloud и другие. Сам класс определяет платформу для запуска и стартует тесты на выбранной платформе. Теперь давайте рассмотрим класс Tests:
public class Tests
{
IApp app;
Platform platform;
public Tests(Platform platform)
{
this.platform = platform;
}
[SetUp]
public void BeforeEachTest()
{
app = AppInitializer.StartApp(platform);
}
[Test]
public void AppLaunches()
{
app.Repl();
}
}
Метод с атрибутом SetUp и будет запускать наши тесты, передавая в StartApp плаформу. К сожалению, “изкоробочный” вариант будет работать только для проекта на Xamarin.Forms. А на MVVM-Cross можно выкрутится как-то так:
public void SetUp ()
{
#if TestiOS
Path = "path/to/app";
_app = ConfigureApp.iOS
.AppBundle(Path)
//Xcode ->Window ->Devices -> Identifier
.DeviceIdentifier("Identifier")
.StartApp();
#else
Path = "path/to/apk";
_app = ConfigureApp
.Android
.ApkFile (Path)
//.EnableLocalScreenshots ()
.StartApp ();
#endif
}
Соответственно, нужно создать таргеты TestiOS и TestDroid.
Ну что ж… настроили, запустили, самое время начать писать тесты!
Тесты обычно имеют вполне стандартную структуру Arrange-Act-Assert тот же Nunit, только в профиль. Следует начать с создания теста, сдержащего метод Repl (read-eval-print-loop). Он запускает консольку, в которой с помощью запросов мы можем увидеть структуру видимой части нашего приложения. Делается это вот так:
[Test]
public void AppLaunches()
{
app.Repl();
}
Данный метод вызывает консоль REPL. Для наглядности я выполнил в ней комманду tree, выдодящюю информацию по всем видимым элементам:
А само приложение при этом выглядит вот так:
Для того чтобы взаимодействовать с управляющими элементами, нам нужно использовать методы интерфейса IApp. В данной статье мы рассмотрим всего три из них: Tap(), WaitForElement() и Query(). Хотя их значительно больше, и полный их список со всеми свойствами можно посмотрть здесь.
Для начала нам нужно получить идентификаторы элементов типа AppQuery. Делается это примерно так:
static readonly Func<AppQuery, AppQuery> InitialMessage = c => c.Marked("MyLabel").Text("Hello, Habrahabr!");
static readonly Func<AppQuery, AppQuery> Button = c => c.Marked("MyButton");
static readonly Func<AppQuery, AppQuery> DoneMessage = c => c.Marked("MyLabel").Text("Was clicked");
Итак, мы получили AppQuery для всех элементов и теперь можем с ними взаимодействовать. Давайте же провалидируем стартовое сообщение, тапнем по кнопке и провалидируем полученное сообщение.
[Test]
public void AppLaunches()
{
app.Repl();
// Arrange - Nothing to do because the queries have already been initialized.
AppResult[] result = app.Query(InitialMessage);
Assert.IsTrue(result.Any(), "The initial message string isn't correct - maybe the app wasn't re-started?");
// Act
app.Tap(Button);
app.WaitForElement (DoneMessage, "Timeout", TimeSpan.FromSeconds (2));
// Assert
result = app.Query(DoneMessage);
Assert.IsTrue(result.Any (), "The 'clicked' message is not being displayed.");
}
Как видно из примера, методы IApp (экземпляром в данном сучае является app) принимают в качестве входных параметров данные типа AppQuery и возвращают результат типа AppResult[]. Linq выражения при этом применимы только к AppResult[], в связи с чем ассерты выглядят следующим образом. А сам AppResult[] выглядит вот так:
В данном примере при локальном запуске тесты могут запускаться паралельно на обеих платформах, и все результаты будут сыпаться в одну консоль (что, имхо, не очень удобно). Кроме того, можно в пару кликов интегрировать замариновский сервис TestCloud и запускать тесты в облаке. Запуск тестов производится из панели юнит тестов, что довольно удобно.
Помимо агента для запуска тестов в TestCloud необходимо добавить api key (в данном семпле я его не добавлял). Как его получить и применить подробно описано здесь.
На этом, пожалуй, стоит завершить наш краткий экскурс в Xamarin.UITest. Надеюсь, мой обзор был вам полезен. Всем спасибо за внимание.
Ссылка на семпл, который использовался в статье.
Полезные ссылки по теме:
developer.xamarin.com/api/namespace/Xamarin.UITest
developer.xamarin.com/guides/testcloud/uitest/intro-to-uitest
www.nunit.org/index.php?p=quickStart&r=2.6.3
msdn.microsoft.com/ru-ru/library/bb397926.aspx