[Из песочницы] Xamarin.UITest

В этой статье я расскажу вам о Xamarin.UITest — фреймворке для приемочного UI тестирования от Xamarin.

Что это такое и как его готовить


Xamarin.UITest сделан на основе Calabash и имеет сходный с ним принцип работы. Однако, тесты на данном фреймворке пишутся на С# и NUnit (в отличие от Calabash, в котором используется Gherkin+Ruby). Фреймворк кроссплатформенный и, следовательно, он позволяет писать тесты как для платформы iOS, так и для Android. Windows Phone не поддерживается, что я лично считаю недостатком данного инструмента. Зато в нем вполне поддерживается тестирование нативных приложений на Java и Objective-C.

image


Почему именно Xamarin.UITest


Конечно, многие из вас могут возразить, мол, для Java и Objective-C существуют свои нативные фреймворки, которые прекрасно справляются с возложенной на них задачей. Но если вам необходимо написать приемочные UI тесты одновременно для двух платформ, Xamarin.UITest придется как нельзя кстати. Более того, в нем есть изкоробочная поддержка Xamarin Test Cloud. Сам тестклауд — штука весьма полезная, но довольно дорогая.

Подробнее о том, как это работает


Механизм работы на iOS и Android отличатется. На iOS в приложение встраивается Nuget пакет, который поднимает http сервер внутри вашего приложения. Вот как это работает на iOS:

177fed39e30d41c38b26aa2e7fb3672d.png

При работе с Android дела обстоят немного иначе. По сути, при сборке и запуске тестов на устройство устанавливаются два приложения: ваше и Test Cloud server. Оба они подписываются одним и тем же ключом, поэтому Test Cloud server имеет права на управление вашим приложением. Выглядит это следующим образом:

6b4cec2498c649c7a33baa1e2bb70f4a.png

Встраивается Xаmarin.UITest в солюшн довольно просто, практически за пару кликов. Всего-то и нужно, что создать еще один проект для UI тестов.

f26e54fa4ac44f8bae93fee3356a4cb9.png

После этих нехитрых действий добавляется проект с шаблоном инициализации и установленными пакетами. Затем, в 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, выдодящюю информацию по всем видимым элементам:

5979638975bb4a628338c079284a4894.png

А само приложение при этом выглядит вот так:

1dcb940d16034cfea64926f2f7dc6023.png

Для того чтобы взаимодействовать с управляющими элементами, нам нужно использовать методы интерфейса 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[] выглядит вот так:

643d26cf932846a8a39797e7019813e9.png

В данном примере при локальном запуске тесты могут запускаться паралельно на обеих платформах, и все результаты будут сыпаться в одну консоль (что, имхо, не очень удобно). Кроме того, можно в пару кликов интегрировать замариновский сервис 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

© Habrahabr.ru