Виктор Мацкевич

Тестирование при помощи библиотеки JUnit для платформы Android

Основной подход к разработке тестов
Рассмотрим применение библиотеки JUnit для тестирования Андроид приложения. В качестве тестируемого приложения был написан простой калькулятор (activity_main.xml и MainActivity.java).

Кратко опишем алгоритм работы приложения. Вводим в поле первое число, после чего выбираем тип операции (сложение, умножение, деление или вычитание). После этого введенное значение сохраняется, а поле ввода затирается. Вводим в него второе число, после чего жмём кнопку «Calculate». В текстовом поле выводится результат. Пример представлен на изображении ниже.
В классе MainActivity. java представлена вся логика работы приложения. Стоит помнить, что для того, чтобы легче было покрыть весь функционал приложения тестами, необходимо разбить функционал на методы.

Также стоит обратить внимание на аннотацию к @VisibleForTesting, она указана как у экземпляров классов, так и у методов. Данная аннотация позволяет обращаться к объектам или методам в тестах. Основной особенностью является то, что если обращаться к этим объектам не из тестов, то по умолчанию параметром доступа будет является «private». Также можно указать режим доступа вручную. Например:
Перейдём непосредственно к самому тесту. Сперва зайдем в build.gradle и вставим следующие зависимости:
Синхронизируем проект, тем самым подтянув добавленные нами зависимости.

После этого заходим в наш класс, который мы хотим протестировать, и выбираем "Create Test".
Появится диалоговое окно:
Выберем библиотеку для тестирования JUnit 4. Ставим галочки напротив setUp и tearDown. Данные методы являются методами жизненного цикла теста. После чего жмём кнопку «OK». Здесь мы выбираем директорию с наименованием «androidTest».

Более подробнее об этих директориях можно прочесть на этой странице.
В результате мы получили новый класс MainActivityTest.java. Собственно здесь и будут располагаться наши тесты.

Но на этом ещё не всё. Нам также необходимо выполнить ряд действий:
  1. Создать объект класса ActivityTestRule, а также добавить аннотацию @Rule к этому объекту. Данный объект является указателем на объект тестирования.
  2. Реализуем методы setUp() и tearDown(). В методе setUp() мы получаем activity, а в методе tearDown() мы присваиваем null данному объекту.
  3. Наследуем наш класс от класса Assert. В классе Assert представлен ряд методов для сравнений объектов. Данный класс значительно упростит работу с тестами
Аннотация @Before — метод, обозначенный данной аннотацией, вызывается перед началом выполнения теста. Обычно в теле метода, обозначенного данной аннотацией, выполняются все подготовительные операции. Например, загрузка валидных данных и т. д.
Аннотация @After вызывается непосредственно после завершения теста.
Первый тест
Напишем первый тест. Нашей задачей является проверить корректность работы метода cleanData() класса MainActivity.java.

Для этого добавим следующий фрагмент кода в класс MainActivityTest.java.
Название теста достаточно субъективно. В данной статье предложен вариант оформлять название теста следующем образом:
[Name function]_[Keys]_[Expected result]
где [Name function] — название основной функции для тестирования,
[Keys] — значения, подаваемые на вход тесту. Если объектов много, то не стоит перечислять все, а попробовать логически сгруппировать.
[Expected result] — ожидаемый результат

В нашем случае можно прочесть название теста следующим образом: тест функции cleanData() вводим «2». после жмём «+» и вновь вводим двойку, в результате выполнения функции cleanData() все объекты будут приведены в исходное состояние.

Теперь рассмотрим последовательно фрагменты кода теста и распишем их более детально.

Функция assertNotNull(Object) проверяет объект на null. Советую не пренебрегать данным методом и использовать его перед вызовом функций у объекта. Типичная проверка объекта.

Чуть ранее было сказано про аннотацию @VisibleForTesting. Давайте добавим в класс MainActivity. java следующие методы c применением данной аннотации:
Данные методы пригодятся нам в процессе написания теста.

Вернёмся обратно к классу MainActivityTest.java. Сперва получаем View объекты:
После чего используем метод runOnUiThread(Runnable action). Выполняем ряд действий. Стоит помнить, что тесты запускаются не в основном потоке, а все действия, связанные непосредственно с View, должны вызываться в основном потоке. Чтобы избежать ошибки, мы и используем данный метод.
В принципе, любые действия, связанные с интерфейсом, можно назвать моделью поведения пользователя
Виктор Мацкевич
Android-разработчик
Проверяем, правильно ли были представлены введённые данные, следующими операциями:
После чего получаем кнопку очистки поля и имитируем нажатие на кнопку методом performClick():
После нажатия должен сработать метод cleanData(). В данном методе происходит обнуление объектов, используемых как в интерфейсе, так и в ходе вычисления результата.
После выполнения всех операций мы должны получить следующий результат:
Данный лог информирует нас о том, что тест пройден успешно.

Также в коде теста вызывается функция getInstrumentation()
.waitForIdleSync()
. Данная функция необходима для синхронизации потоков. Однако она ожидает завершения всех потоков, связанных с UI. Именно поэтому она не всегда будет срабатывать при выполнении тестов с асинхронными задачами. Этой теме будет посвящена отдельная статья.

Есть ещё немало особенностей в написании тестов, о них поговорим позже.
Спасибо за внимание!