Continuous Integration для Android

После реализации пары проектов под Android, стало понятно, что, несмотря на их небольшие размеры и кажущуюся простоту, без автоматизированных сборок и тестирования не обойтись. Родилось желание получить следующие возможности:
  • unit тестирование,
  • интеграционное тестирование,
  • тестирование интерфейса,
  • тестирование этих типов тестов на различных версиях Android и конфигурациях девайсов,
  • рассылка по email в случае сломанного билда;
  • таже не помешает автоматическая сборка релизов.

Почти сразу же попался сайт testdroid.com, которые предлагает почти всё то же, и даже больше (тестирование на реальных девайсах), но, естественно, за денюжку.
В связи с этим после двухнедельных изысканий выбор был остановлен на связке на Jenkins + билд-скрипты на Ant (под Windows).

Итак, как же всё это поднять?!

Предварительные условия:


  1. Пусть у нас есть уже созданный, каким-либо образом, проект MyProject. И к нему проект для тестирование MyProjectTest (в тестовом проекте есть хоть один тест). И пусть лежат папки с проектами в директории d:\projects\MyProject\trunk\. Исходники проекта у меня хранятся под SVN.
  2. Данный мануал написан при использовании Android SDK версии 16 (http://developer.android.com/sdk/index.html). Если у вас другая версия, возможно что-то придётся сделать по-другому.

Шаг 1. Установка переменных окружения (необязательный, но так проще).


Создаём в окружении системы (System variables) переменную ANDROID_HOME и указываем в её значеии путь до папки Android SDK. У меня это путь D:\android\android-sdk-windows.
Также добавляем в переменную Path значнеие ;%ANDROID_HOME%\platform-tools.

Шаг 2. Установка Ant


Скачиваем zip-архив с http://ant.apache.org/bindownload.cgi и распаковываем в какую-нибудь папку (например d:\server\android\ant). Папку ant\bin также необходимо добавить в системную переменную окружения Path.

Шаг 3. Подготовка build-скрипта для ant.


Google сделала в Android SDK возможность генерировать ant-скрипты для сборки проектов, для этого в папке trunk проекта нужно выполнить команды:
  1. Для создния build-скрипта для основного проекта:
    1. android update project -p MyProject

  2. Для создания build-скрипта для тестового проекта:
    1. android update test-project -m ..\MyProject -p MyProjectTest

Варианты для Android SDK < 14 версии можно посмотреть здесь.

Шаг 4. Проверим сборку проекта (опять необязательный).


  1. Зайдите в папку trunk\MyProject\ и выполните команду:
    1. ant clean debug
    в конце выполнения должно быть написано что-ть вроде
    1. BUILD SUCCESSFUL
    2. Total time: 5 seconds
    Это значит, что проект удалось успешно собрать.
  2. Запустите эмулятор Android и в папке trunk\MyProjectTest выполните
    1. ant all clean emma debug install test
    Билд будет идти несколько дольше, в моём случае это было 35 секнунд. В процессе соберутся debug сборки MyProjeсt и MyProjectTest, установятся на запущенный эмулятор и произведётся прогон тестов.

Шаг 5. Установка Jenkins.


Заходим на http://jenkins-ci.org/, там справа есть ссылки на дистрибутивы, скачиваем и устанавливаем. По-умолчанию он устанавливается и предоставляет веб-интерфейс по адресу http://127.0.0.1:8080/.

Тут есть небольшая проблема. По умолчанию сервис Jenkins стартует из-под пользовательского аккаунта System, что ведёт к невозможности для плагина, рулящего эмуляторами, создавать AVD в домашней папке аккаунта. Пользователя нужно поменять либо на ваш аккаунт, либо создать специальный аккаунт для Jenkins’а.

Если кто вдруг не знает, для этого нужно зайти в Computer Management (самы простой способ: Start > Computer (правой кнопкой) > Manage (Управление)), там выбираем Services and Applications > Services, находим сервис Jenkins > Properties > закладка LogOn. После смены аккаунта сервис необходимо перезапустить.

Шаг 6. Настройка Jenkins.


Итак, захоидм по адресу http://127.0.0.1:8080/, выбираем Manage Jenkins далее выбираем Manage Plugins. Перейдите на вкладку Available Plugins. Если тут пусто, подождите некоторое время, Jenkins их просто ещё не подгрузил.
Нам понадобятся следующие:
  • Jenkins Emma plugin
  • Android Emulator Plugin
  • Hudson Port Allocator Plug-in
  • ant

Возможно некоторые из них у вас уже буду установлены изначально.
В моём случае потрбовалось ещё установить Subversion Plugin, поскольку у меня проект лежал в SVN. Если ваш проект находится под другой SCM, не забудьте установить для неё плагин.
Теперь, нажимаем кнопку «Install without restart” внизу, в процессе можно поставить галку »Restart Jenkins when installation is complete and no jobs are running” чтобы Jenkins перезапустился автоматом и плагины заработали.
Далее идём в Manage Jenkins > Configure System. Здесь
  1. в секции Android нужно прописать Android SDK root (как уже писалось выше, в нашем случае это D:\android\android-sdk-windows)
  2. Для рассылки email при поломке билда так же стоит заполнить секцию E-mail Notification.

Теперь создадим задание для Jenkins. На главной странице нажимаем New Job, вводим название, тип задания выбираем Build a free-style software project.

Тут необходимо заполнить следующие секции:
  1. Source Code Management — выбираем тип вашей системы контроля версий и вводим необходимые параметры. Внимание! Необходимо, чтобы из репозитария вытягивалась только папка trunk, иначе вам нужно будет подправлять все пути, которые будут дальше в настройках задания для Jenkins.
  2. Build Triggers — выбираем Poll SCM и прописываем туда * * * * *. Это будет означать, что Jenkns будет проверять репозитарий раз в минуту и, если там будут новые коммиты, запускать билд. Для сборки раз в 5 минут можно указать */5 * * * *.
  3. Build Environment — Тут лучше поставить галочку Assign unique TCP ports to avoid collisions.
  4. Run an Android emulator during build. Заполняем параметры эмулатора, например
    • Android OS Version: 2.1 (Убедитесь, что данная версия скачана у вас в SDK)
    • Screen density: 240
    • Screen resolution: WVGA
    • Device locale: en_US
    • SD card size: 16M
  5. Раздел Build, добавляем Invoke Ant, в здесь
    • Target: all clean emma debug install test
    • Build File: QuickMeetingTest\build.xml
    • Properties: sdk.dir=D:\\android\\android-sdk-windows (именно так, с двойными слешами)

  6. Раздел Post-build Actions
    • Archive the artifacts: **/*test-TEST.xml
    • Publish JUnit test result report: **/*test-TEST.xml
    • Record Emma coverage report: **/coverage.xml
    • Record fingerprints of files to track usage: **/*test-TEST.xml

  7. E-mail Notification — настроить по своему вкусу.

Собственно, с настройкой Jenkins почти закончили, но не спешите, пока ещё не всё готово.

Есть две проблемы.

Проблема 1.

Стандартный InstrumentationTestRunner не генерирует xml с результатами прохождения тестов. Поэтому MyProjectTest надо чуть подкрутить.
  1. Идём на http://code.google.com/p/the-missing-android-xml-junit-test-runner/ Качаем оттуда polidea_test_runner_1.1.jar, кладём в trunk\MyProjectTest\libs и добавляем Java Build Path\Libraries в свойствах проекта.
  2. В AndroidManifest.xml меняем <instrumetation>, чтобы оно выглядело так:
    1.  <instrumentation
    2.        android:name="pl.polidea.instrumentation.PolideaInstrumentationTestRunner"
    3.        android:targetPackage="com.example.MyProject" />

  3. В файле project.properties добавляем строчку
    1. test.runner=pl.polidea.instrumentation.PolideaInstrumentationTestRunner

  4. И в строке
    1. <!-- version-tag: 1 -->
    меняем 1 на custom (нужно, чтобы ваши изменения в этом вайле не перетёрлись при следующем выполнении android update).

Проблема 2.

Xml-файл с результатами прогона тестов и файл с результатами проверки покрытия генерируется на устройстве. Их необходимо оттуда достать, чтобы Jenkins их подхватил.
Основной скрипт ant, который собственно и собирает проект находится в ${sdk.dir}/tools/ant/build.xml и импортируется в билд-скрипт проекта строчкой
  1. <import file="${sdk.dir}/tools/ant/build.xml" />

  1. Идём в ${sdk.dir}/tools/ant/build.xml, берём оттуда всю секцию <target name=«test» ../> и вставляем в наш build.xml в тестовом проекте до вышеуказанного импорта основного скрпта.
    Это переопределит вызов цели test и, когда мы модифицируем её, будут работать наши изменения.
  2. В <target name=«test» ../> в нашем build.xml ищем
    1. <html outfile="coverage/coverage.html" />

    и рядом добавляем
    1. <xml outfile="coverage/coverage.xml" />

  3. А в конце, перед </target> вставляем код для вытягивания результатов тестирования с эмулятора:
    1. <mkdir dir="${basedir}/junit-results" />
    2. <exec executable="${adb}" failonerror="true" dir="${basedir}/junit-results">
    3.     <arg line="${adb.device.arg}" />
    4.     <arg value="pull" />
    5.     <arg value="/data/data/${tested.manifest.package}/files/" />
    6. </exec>

Шаг 7. Добавление тестов UI.


Для этого есть удобный инструиент Robotium. Взять можно здесь code.google.com/p/robotium.
Настройка:
  1. Скачиваем robotium-solo-3.1.jar (на данный момент это последняя версия), кладём в trunk\MyProjectTest\libs.
  2. Добавляем в свойствах проекта в Java Build Path\Libraries
  3. Создаём тестовый класс и наследуем его от ActivityInstrumentationTestCase2<MyActivity>, в конструкторе должен быть вызов вида
    1. super("com.example.myproject", MyActivity.class);

    где «com.example.myproject» — пакет тестируемого проекта, MyActivity — тестируемая Activity.
  4. Добавляем в класс строчки
    1. private Solo solo;
    2.  
    3.         @Override
    4.         public void setUp() throws Exception {
    5.                 solo = new Solo(getInstrumentation(), getActivity());
    6.         }
    7.        
    8.         @Override
    9.         public void tearDown() throws Exception {
    10.                 solo.finishOpenedActivities();
    11.         }

и далее можно писать тесты вида
  1. public void testPreferenceIsSaved() throws Exception {
  2.  
  3.                 solo.sendKey(Solo.MENU);
  4.                 solo.clickOnText("More");
  5.                 solo.clickOnText("Preferences");
  6.                 solo.clickOnText("Edit File Extensions");
  7.                 Assert.assertTrue(solo.searchText("rtf"));
  8.                
  9.                 solo.clickOnText("txt");
  10.                 solo.clearEditText(2);
  11.                 solo.enterText(2, "robotium");
  12.                 solo.clickOnButton("Save");
  13.                 solo.goBack();
  14.                 solo.clickOnText("Edit File Extensions");
  15.                 Assert.assertTrue(solo.searchText("application/robotium"));
  16.                
  17.   }

Подробности можно почитать тут code.google.com/p/robotium/w/list.

Шаг 8. Тестирование на нескольких эмуляторах.


  1. При создании задания для Jenkins можно выбрать тип задания Build multi-configuration project.
  2. В разделе Configuration Matrix создаём оси для каждого изменяющегося параметра, например, разрешение и версия андроид и подставляем в настройки Run emulator with properties в виде ${resolution}. Посмотрите картинки по вышеуказанной ссылке, всё станет понятно.
  3. Остальные параметы указываем как в обычном задании.

Подробности тут.

Шаг 9. Подготовка релизной сборки.


Релизную сборку собрать попроще, чем запустить тесты.
  1. Создаём в Jenkins задачу MyProjectRelease (тип Build a free-style software project).
  2. Как в шаге 6, указываем откуда тянуть исходники.
  3. Создаём Invoke Ant Build Step с параметарми:
    1. Targets:
    2.     release
    3. Build File:
    4.     MyProject\build.xml
    5. Properties:
    6.     sdk.dir=D:\\android\\android-sdk-windows
    7.     key.store=..\\myproject.keystore (относительный путь к файлу-хранилищу ключа, сам ключ я положил в trunk\)
    8.     key.alias=<alias>
    9.     key.store.password=<pass>
    10.     key.alias.password=<pass>
  4. Можно ещё настроить Archive the artifacts с Files to archive = **\*-release.apk (если вы хотите бережно хранить все релизы и иметь возможность их скачивать через веб-интерфейс Jenkins).
  5. Для обфускации релиза при помощи ProGuard потребуется всего лишь добавить в project.properties (или в properties шага InvokeAnt задания в Jenkins) строчку proguard.config=proguard.cfg, где
    • proguard.config — имя переменной, в которой должно находиться имя файла с настройками для ProGuard,
    • а файл proguard.cfg автоматически создаётся Eclipse при создании проекта.

Спасибо за внимание, надеюсь этот пост поможет сделать ваши программы чуточку лучше.

Также отдельное спасибо за поддержку руководству компании Артезио, которое поддержало мою инициативу и выделило время на данное исследование.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 18

    0
    Поправка: достаточно одного действия для выставления значения test.runner.
    Т.е. или в project.properties пишете или в самом build.xml. В двух местах писать одно и то же смысла нет
      0
      Да, спасибо, поправил. И заодно вернул спрятавшуюся «Проблему 2» в шестом шаге.
      0
      Спасибо большое, как раз то, что надо!
        0
        хорошая инструкция, буду возиться на выходных ) давно думал получить левел ап )
        +1
        Почему выбран ant а не maven? Для maven есть отличный плагин для android. Сам делал CI недавно при помощи связки Jenkins + Maven
          0
          Просто гугл сделал уже готовые ant-скрипты (команда «android update project», Шаг 3), и есть надежда, что они будут их нормально обновлять в следующих SDK.
            0
            Есть ли реальный смысл использовать maven для android?
            Пробовали его на одном проекте, но поимели больше проблем с настройкой, чем профита.
              +3
              а до этого вообще использовали его плотно не на андроиде? т.к. «больше проблем» часто связано с привыканием к мавену вообще :)
                0
                Где можно посмотреть наработки?
                тема очень интересная
                  0
                  да какие там наработки, ставьте nexus в качестве прокси репозитория, деплоите на него свои артефакты и живёте — не тужите.
                  в книгах всё написано, правда, на английском.
                    0
                    это понятно, просто с ними было бы быстрее.
                    Книги есть по этим темам но по отдельности… ;)
                    Ну да ладно будем ковырять.
          0
          спасибо за статью!
            0
            Прекрасная статья — спасибо!
            Пробовал раньше com.neenbedankt.android.test.InstrumentationTestRunner, но что-то не взлетело.
            Не написали, что надо прописать в build.xml (впрочем это должно быть очевидно).
              0
              > Не написали, что version-tag надо прописать в build.xml (впрочем это должно быть очевидно).
                0
                Есть же вроде: Шаг 6. Проблема 1. Пункт 4
                  0
                  Я имел в виду, что не указано что надо менять в build.xml, а просто написано «4. И в строке», в предыдущих пунктах указаны файлы где менять (хотя это мелочь).
              0
              Отличная статья — она очень помогла мне в настройке нашего тест проекта. Хотел бы добавить еще немного касательно multi-configuration project. Мы использовали этот тип проекта в hudson что бы можно было проганять один и те же тесты на различных конфигурациях андроида — OS, density, screen resolution. И вот тут мы столкнулись с одной проблемой — при текущих настройках, hudson отказывался видеть coverage.xml и test results files. Из-за этого он фейлил билд. Долгие поиски и разные вариации указания пути ничего не помогли, пока подсказка не пришла из сети:
              Раздел Build, в Invoke Ant->Properties добавляем workspasedir=$WORKSPACE
              После чего в build.xml file прописывает пути для test results ${workspacedir}/junit-results и соответсвенно для coverage.xml -> ${workspacedir}/coverage/coverage.xml
              После этого hudosn прекрасно видит и парсит результаты. Еще раз спасибо за статью — очень детальная и полезная

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое