Unit тесты для android

Тестирование приложения

Немного поработав в Android Studio и разобравшись в его структуре, вы, наверняка, замечали папки androidTest и test рядом с основной папкой с классами вашего проекта. Возможно, вы также слышали выражение от умных программистов, что приложение полностью покрыто тестами.

Зачем нужно тестирование и нужно ли оно вам? Если вы начинающий программист и пишете простые учебные примеры, то тесты вам не нужны. Даже если вы перешли от простых примеров к собственным разработкам средней сложности, то вполне можете обходиться без тестирования и дальше. Почему? Дело в том, что в небольших проектах вы в состоянии контролировать логику приложения. Вы можете предугадать слабые места и поправить код. Даже если вы что-то пропустили и выложили релиз в открытый доступ, то по отзывам пользователей вы можете быстро сориентироваться в проблеме и исправить ошибку.

Но всё меняется, если программа стала сложной. У вам появилось больше десятка различных экранов активностей, отдельных классов и т.д. Вы аккуратно стали разбивать код на модули, чтобы обеспечить независимость. Такой подход в обязательном порядке используется в компаниях, где каждый отвечает за свой участок кода. И тут вас подстерегает опасность. Вам доверили написать новый метод или переписать существующий метод, но вы ещё не до конца уяснили взаимодействие метода с другими кусками кода. Если метод в проекте был давно, то все уверены в его работе и фокусируются на новых задачах. Но вы сделали ошибку, например, банально опечатались и вместо плюса поставили минус — и числа стали вычитаться вместо сложения. Причём выводимые результаты могут быть вполне корректными для приложения. Поэтому при запуске приложения с неправильным методом программисты могут не заметить проблему. Проблему могут заметить позже сами пользователи, но если это приложение связано с банковским сектором, то несколько миллионов рублей, исчезнувших со счёта, не прибавит радости ни пользователю, ни руководству.

Как помогло бы тестирование. Для метода заводится проверка на сложение двух контрольных чисел, пусть будет 6 и 3. Когда группа программистов внесла свои правки в код, то запускается тестирование методов, в том числе и вашего метода. Теперь если вместо 9 (6 + 3) метод выдаст 3 (6 — 3), то тест не будет пройден. И вы будете сразу искать проблему в нужном месте.

Приблизительно так работают тесты — проверка на соответствие к ожидаемому результату.

Тесты делятся на две категории — локальные (Unit Testing) и инструментальные (UI Testing).

Unit Testing

Локальные тесты проверяют работу метода, класса, компонента. Тест не зависит от Android, по сути вы проверяете код Java, который можно проверить на обычном компьютере без участия устройства или эмулятора. Например, такому варианту соответствует сложение двух чисел типа int. Подобные тесты проводят в папке Test.

Популярными инструментами для юнит-тестов являются:

В build.gradle модуля приложения имеется строка для компиляции юнит-тестов.

UI Testing

Для инструментальных тестов наличие устройства или эмулятора обязательно, так как нужно тестировать нажатие кнопки, ввод текста, прокрутку, касания и другие операции. Тесты проводят в папке androidTest.

Инструменты для тестирования:

  • Espresso
  • UIAutomator
  • Robotium, Appium, Calabash, Robolectric

В build.gradle тесты представлены в виде строки.

Часто для удобства приложение разделяют на несколько вариантов.

В build.gradle в секцию android добавляют блок

Указав другой идентификатор для mock, вы можете устанавливать вместе две разные версии приложения на одном устройстве. Рабочая версия будет использовать настройки по умолчанию из defaultConfig.

Читайте также:  Mind map android 4pda

Источник

Android курс в Технополисе 2019

Сначала немного скучной теории:

Когда можно применять.

  1. Когда нужно эмулировать сложные ошибки, использовать знание о коде для того чтобы проверить все ветвления приложения и различные состояние объектов которые крайне тяжело эмулировать в e2e тестировании.
  2. Когда приложение еще не готово. При написании Unit тестов нет необходимости дожидаться готовности кода всего приложения или даже отдельного компонента, можно писать тесты только для одного метода.
  3. Когда надо быстро находить ошибки. Если ошибка существует, то для ее обнаружения и устранения как правило понадобится меньше времени, чем при дебаггинге приложения.
  4. Когда добавление очередного Unit теста вызывает затруднение, либо Unit тест получается громоздким и запутанным. Также, если нет возможности проверить метод в изоляции, вероятнее всего код имеет высокую связность (high coupling)
  5. Когда надо вспомнить как работает метод. Unit тесты являются своеобразной «документацией в примерах» для кода проекта.

Первый JUnit тест

Unit-тесты в андроиде по умолчанию хранятся в src/test/java. Другое место хранения можно настроить в секции sourceSets в gradle файле приложения.

На рисунке видно, что мы добавили еще папку test_utils . Она тоже будет использоваться при выполнения тестов. Там будут лежать утилиты. Также на рисунке видно, что для тестовой конфигурации используется конструкция testImplementation. Если какая-то билблиотека нужна для Unit-тестов, то подключаем ее или в testImplementation или в testApi.

В приложении Testing example есть класс Calculator , который выполняет вычисления:

Это чистый Java код. В нем нет ничего от Android. А значит мы можем создать Unit тест, который будет для нас тестировать этот класс. Самый простой способ создать Unit-тест. Это кликнуть правой кнопкой в теле класса и выбрать Go To. Дальше можно выбрать куда добавить новый тест. Это может быть как новый класс, так и существующий.

Дальше будет “мастер”, в котором можно выбрать:

  • тип теста. Мы сейчас говорим о JUnit4, который является стандартом.
  • Название класса с тестами. По умолчанию к классу добавляется суффикс Test.
  • Класс родитель, если есть.
  • package. Важная вещь. Если тестовый класс находится в таком же пакете как и класс приложения, то мы будем иметь доступ к его protected и package-private методам. В противном случае, мы сможем обращаться только к public методам. Без ухищрений.
  • Дальше можно выбрать создавать ли нам фикстуры для настройки состояния тестов.
  • Можно выбрать методы для которых писать тесты.

Я создам один тест в классе SimpleCalculatorTest `

  1. Аннотация @RunWith показывает какой раннер будет использован для запуска тестов. В простейшем случае, когда используются только Junit4 тесты, эту аннотацию можно опустить. Раннер должен знать как ему искать тесты. Для JUnit4, раннер будет искать все методы внутри класса, которые помечены аннотацией @Test .
  2. Нужен public класс, который выступит хранилищем тестов SimpleCalculatorTest .
  3. Это аннотация для обозначния фикстуры (о них ниже).
  4. Сам тест.

Сначала создаем экземпляр класса Calculator. На нем будем проводить тест Складываем два числа 2 и 3, они запишутся в поле result внутри класса Calculator. Методом assertEquals сравниваем ожидаемый результат и то, что вернет нам Сalculator. Если значения окажутся не равны, то при запуске теста метод assertEquals выбросит ошибку.

Фикстуры

Фикстуры — фиксированные состояния объектов используемые в качестве исходных данных при запуске тестов. Фикстуры позволяют многократно использовать один и тот же исходный код, в случае, если исходные данные для нескольких тестов одинаковые.

  1. Аннотация @Before обозначает методы, которые будут вызваны до исполнения теста, методы должны быть public void. Здесь обычно размещаются предустановки для теста, в нашем случае это получение инстанса Calculator.
  2. Аннотация @BeforeClass обозначает методы, которые будут вызваны до создания экземпляра тест-класса, методы должны быть public static void. Это однократная
  3. Аннотация @After обозначает методы, которые будут вызваны после выполнения теста, методы должны быть public void. Здесь размещаются операции освобождения ресурсов после теста, .
  4. Аннотация @AfterClass связана по смыслу с @BeforeClass, выполняется один раз после прогона всех тестов.
Читайте также:  Как создать калькулятор для андроида

В классе FixturesExampleWithRule есть следующий код:

Здесь два теста и 4 фикстуры. После запуска класса в консоль выведется следующее:

Правила

Кроме всего вышеперечисленного есть довольно интересная вещь — правила. Правила это некое подобие утилит для тестов, которые добавляют функционал до и после выполнения теста.

Например, есть встроенные правила для задания таймаута для теста(Timeout), для задания ожидаемых исключений(ExpectedException), для работы с временными файлами(TemporaryFolder) и д.р. Для объявления правила необходимо создать public не static поле типа производного от MethodRule и зааннотировать его с помощью Rule. Пример в RulesExample классе.

Правила выполняются после @Before и после @BeforeClass

Дополнительные аннотации для тестов

Можно извлечь дополнительную функциональность из аннотации @Test

  • Тест будет работать не больше секунды, а потом упадет @Test(timeout = 1000)
    *Аннотация для перехвата исключения @Test(expected = IllegalStateException.class)
    *Тест не будет выполняться @Ignore(«Разберусь потом») Пример в TestAnnotationParameters классе.

##Утверждения В каждом тесте должно быть хотя бы одоно утверждение (assert). Они необходимы иначе тест будет постоянно проходить и будет бесполезен.

Пройдемся по параметрам:

  • message — это сообщение, которые будет выброшено при наступлении ошибки. Лучше написать его наиболее понятным, чтобы быстрее разобраться в корне проблемы.
  • condition — это условие, которое должно вернуть boolean . Возьмем пример с вычитанием.

Гугл рекомендует юзать свою либу для ассершена Туториал к Google Truth. Также есть библиотека Туториал к hamcrest либе, в которой много много дополнительных матчеров. Она намного чаще используется в паре с JUnit’ом.

Как запускать тесты

Запускать можно с помощью кнопки play. В меню можно выбрать нужный метод запуска. С помощью кнопки плэй можно запустить как один тест в классе так и все тесты внутри класса. Также тесты можно запустить с помощью gradle. Так запустятся все тесты во всех модулях.

Можно добавить фильтрации с помощью ключа —tests

Моки и стабы

Не всегда есть возможность создать все объекты для теста функции:

  1. Иногда это слишком сложно из-за большой вложенности параметров и объектов.
  2. Иногда нужного класса еще нет или он может поменять поведение.
  3. Иногда объект производит неопределенные результаты (например текущее время) Для того чтобы справиться с этими ситуациями были придуманы Моки (объекты заглушки, тестовые дубли) Такой объект иммитирует поведение объекта или может добавлять дополнительную информацию к нему (Spy)

Немного скучной теории:

    Dummy object — когда реализация объекта совсем не важна, ее даже можно заменить на null

Test Spy — реальный объект, чьи методы можно переопределить.

  • Fake Object — специальный объект, который может выглядеть как реальный, но сконструирован только для теста. В полях может быть билиберда сгенирированная
  • Самая популярная библиотека для создания Моков — это Mockito Для ее работы надо добавить ее в build.gradle

    В тестах, в классе ru.android2019.testingexample.emailValidator.SharedPreferencesHelperTest будем использовать Mockito чтобы создать заглушку для SharedPreferences . Т.к. SharedPreferences это часть Android API мы не сможем протестировать ее в локально JVM, поэтому мы сделаем объект, который будет повторять нужное нам поведение и выдавать себя за SharedPreferences .

    Мокированный объект можно создать двумя способами:

      Вызвать статический метод mock на прямую. Создастся пустой объект, который не умеет ничего делать.

    Методы Mockito

    Другие методы, которые предоставляет Mockito мы посмотрим немного в отрыве от Android

      @Spy — эта аннотация создает обертку вокруг реального объекта. Т.е. в отличие от @Mock у нас получится не пустая заглушка, а полноценный ArrayList со всеми методами, но мы также можем переопределять нужные нам методы. В примере ниже был переопределен метод size()

    PowerMock

    Мокито не умеет делать стабы на статические или приватные методы (в последних версиях это добавили, но фича позиционируется, как incubating). Для этого используется библиотека PowerMock она поставляется как расширение для Mockito. На данный момент Mockito 2 поддерживается в экспериментальном режиме и иногда приходтся помучаться с зависимостями. Зато с помощью PowerMock можно протестировать практически любой код, даже который не спроектирован для тестирования.

    Пример можно посмотреть здесь ru.android2019.testingexample.powerMockExample.PowerMockTest

    Robolectric

    При использовании не замоканного кода Android фреймворка, можно получить исключение. RuntimeException c причиной — method not mocked при попытке запустить тест кода вызывающего какой — либо метод фреймворка.

    Если использовать следующую опцию в Gradle

    то, RuntimeException брошен не будет. Такое поведение может приводить к тяжело детектируемым ошибкам в тестах.

    На помощь приходит Robolectric — это специальная опен-сорсная библиотека, которая состоит из специально скомпилированного android.jar с обвязкой из утилит для запуска тестов и упрощения тестирования. Он поддерживает загрузку ресурсов, примитивную реализацию инфлэйта View (не поддерживает CustomView), предоставляет локальную SQLite (sqlite4java), легко кастомизируем и расширяем. Тесты с использованием Robolectric уже относятся к интеграционным, они стоят между Unit-тестами и Instrumentation тестами. Хранятся они вместе с Unit-тестами, т.к. выполняются на локальной JVM. либо других ресурсов приложения, т.к. они являются неотъемлемой частью бизнес — требований.

    Роболектрик хорошо подходит для тестирования следующих объектов Android FrameWork’а:

    Parcelable — нужно проверять корректоность сериализации и восстановления.

    SQLite — тестирование миграции данных, изменения схем, добавление новых таблиц, корректность выполнения запросов. Распространенный кейс это проверка баз при обновлении приложения.

    Shared Preferences — запись и чтение.

    Intent / Bundle — флаги, с которыми будет запущена следующая Activity или Service.

    В тестовом классе ru.android2019.testingexample.emailValidator.RobolectricSharedPreferencesHelperTest Robolectric сам создает для нас DefaultSharedPreference и можем работать с ним как будто приложение запущено на устройстве.

    Для использования Robolectric надо добавить следующие строчки в build.gradle

    Robolectric может запускать тесты для определенной версии sdk или для ориентации или разрешения экрана, или языка.

    Роболектрик не может работать с:

    • Native кодом — Android native код не может быть исполнен на локальной JVM.
    • Вызовами вне процесса — На локально ймашине не запущены Android system services.

    Роболектрик решает эту проблему с помощью классов, которые называются Shadows. Каждый Shadow объект может модифицировать или расширять поведение нужного класса в Android OS. Когда создается объект Android класса, Robolectric ищет соответствующий ему Shadow класс, если находит, то создает Shadow объект и связывает его с Android обхектом.

    Через инструментирование Байт-кода Роболектрик может создавать кросс-платформенную прокси имплементацию, чтобы заместить ею native код и добавить дополнительное API, чтобы сделать тестирование возможным.

    Возьмем класс ShadowView он является Shadow для класса View из Андроида. При обращении ко View внутри Robolectric теста, будет использован этот класс.

    Роболектрик лучше не использовать для тестов:

    • В которых много перемещений между Activity
    • В которых применяется много потоков и нужна синхронизация между ними.

    Источник

    Читайте также:  Dark rhyme pie v11 тема для андроид
    Оцените статью