Узнайте, как начать писать модульные тесты в Android

В разработке мобильных приложений у нас есть много трендовых вещей, которые находятся в стадии разработки, такие как MVVM, Jetpack, KMM (мультиплатформа Kotlin) и т. д. Тестирование — одна из важных и предпочтительных вещей в настоящее время. Тестирование стало решающим фактором в разработке. Существует несколько стратегий тестирования приложений. В большинстве интервью знание написания юнит-тестов стало обязательным. Итак, в этом посте давайте посмотрим, что такое модульное тестирование и некоторые основы написания сценариев локального модульного тестирования.

Поскольку все не может быть описано в одном посте, я напишу серию статей о тестировании. Если вы хотите перейти к коду, ознакомьтесь с репозиторием Github: android_unit_tests

Нужно ли нам писать Unit Test Cases?

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

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

Что такое модульное тестирование?

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

Как написать простой модульный тест

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

Обычно у нас есть структура проекта в Android Studio, где мы используем

  • Папка main для написания логики приложения.
  • Папка test используется для написания модульных тестов.
  • Папка androidTest используется для написания интеграционных или автоматизированных тестов, которые мы обсудим в следующих сообщениях.

Если мы используем структуру Android, она может немного отличаться, но, как упоминалось выше, мы используем разные каталоги для соответствующих целей.

Например, у нас есть процесс входа в приложение, где мы берем адрес электронной почты пользователя в качестве имени пользователя. Нам нужно подтвердить это письмо, прежде чем использовать наш API для входа. Это необходимо для подтверждения того, что указан правильный идентификатор электронной почты. Для этого мы создаем класс с именем Validator, в котором у нас есть проверка.

Приведенный выше класс Validator имеет метод validateEmail, который принимает входные данные, проверяет их и предоставляет логическое значение в соответствии с результатом проверки. Это мы можем использовать в нашей Activity или Fragment или ViewModel для проверки. Давайте посмотрим, как написать для него базовый модульный тест.

Аннотация @Test сообщает JUnit, что метод public void
, к которому он присоединен, может быть запущен как тестовый пример. Чтобы запустить метод,
JUnit сначала создает новый экземпляр класса, а затем вызывает
аннотированный метод. JUnit сообщает
о любых исключениях, выдаваемых тестом, как об ошибках. Если исключений не выдается, считается, что тест
прошел успешно.

В приведенном выше фрагменте мы используем библиотеку Truth от Google для утверждений. Поскольку мы закончили его писать, нам нужно выполнить тест и увидеть результат. Поскольку это был простой тест, нет необходимости в эмуляторе или устройстве Android, они могут запускать саму JVM.

Мы можем запустить тест, щелкнув файл правой кнопкой мыши и выбрав опцию Run ValidatorTest, или нажав кнопку воспроизведения рядом с методом, чтобы выполнить конкретный метод, или кнопку воспроизведения рядом с именем класса, чтобы запустить все тесты внутри класса.

Как вы можете видеть, это успешный случай, он показывает, что тесты пройдены 1. Чтобы получить их, нам нужно иметь следующие зависимости в файле Gradle сборки на уровне модуля.

testImplementation 'junit:junit:4.12'
testImplementation 'com.google.truth:truth:1.1.3'

При написании тестов всегда одинаково важно проверять все возможные случаи. В приведенном выше случае нам нужно проверить как успешные, так и неудачные случаи. Давайте посмотрим на результат, добавив неправильный регистр электронной почты, который должен возвращать false.

Вот и пройдено 2 теста…

Давайте посмотрим в режиме реального времени на написание модульных тестов Android.

Давайте возьмем обычный пример из нашей повседневной рутины, выполняя вызов API в Viewmodel, и на основе полученного состояния мы отправляем данные в пользовательский интерфейс. Например, давайте продолжим сам пример выше, где у нас есть поток входа. После ввода учетных данных пользователь нажимает кнопку отправки, где мы делаем вызов метода в ViewModel, предоставляя данные входные данные, откуда мы делаем вызов Repository, а в RepositoryImplementation мы используйте службу API, чтобы получить доступ к нашему API.

Написание модульного теста для вышеуказанного было бы простым, если бы мы использовали фиктивные объекты вместо реальных вещей. Насмешка – это процесс имитации или создания нереального поведения. В модульном тестировании имитация помогает изолировать внешние зависимости и создать фиктивное поведение ожидаемых вещей. Проще говоря, предоставление фиктивного или симулированного поведения для проверки логики.

В примере потока входа мы сначала протестируем ViewModel, затем Repo и, наконец, APIService. Поскольку мы используем концепцию насмешек, мы разделяем вещи и тестируем их независимо. Давайте посмотрим, как это сделать.

Проверьте репозиторий Github android_unit_tests для кодовой базы.

Шаг 1. Написание тестов для LoginViewModel

Давайте создадим класс LoginViewModelTest, соответствующий LoginViewModel. LoginViewModel может принимать варианты использования или репозитории в качестве параметров конструкции, поэтому нам нужно имитировать соответствующие классы на основе нашего использования. Предположим, он использует интерфейс репозитория LoginRepo. Простая версия LoginViewModel будет выглядеть так:

Теперь давайте напишем модульные тесты для представленной выше ViewModel.

  1. Загрузка тестовых случаев проверки состояния
  2. Тестовый пример успешного состояния API
  3. Тестовый пример состояния ошибки API

Для имитации LoginRepo у нас есть Mockitoбиблиотека. добавьте следующие зависимости в файл сборки модуля Gradle

testImplementation 'org.mockito:mockito-core:2.28.2'
androidTestImplementation 'org.mockito:mockito-android:2.24.5'

Кроме того, нам нужно аннотировать наш класс LoginViewModelTest с помощью @RunWith(JUnit4::class). При этомJUnit будет вызывать класс, на который он ссылается, для запуска тестов в этом классе вместо бегуна, встроенного в JUnit. Мы можем определить любой пользовательский бегун в зависимости от наших требований.

Давайте посмотрим, как создать фиктивный объект LoginRepo.

val loginRepo = Mockito.mock(LoginRepo::class.java)

Использование Mockito также может использоваться в зависимости от условия с использованием when() и thenReturn(). Простой пример для этого может быть следующим:

Mockito.when(loginRepo.validateLoginDetails("[email protected]","123")).thenReturn(LoginResponse())

Приведенное выше выражение возвращает LoginResponse, когда вызывается метод loginRepo validateLoginDetails, предоставляющий входные данные.

При написании тестов обычно требуется создать или выполнить несколько вещей перед запуском тестов и очистить их после завершения выполнения тестов. В некотором смысле это похоже на методы init и onCleared в Viewmodels. Мы достигаем этого с помощью аннотаций @Before и @After.

Наше LoginViewModelTestвместе будет выглядеть примерно так, как показано ниже.

Поскольку мы используем сопрограммы Kotlin, нам может потребоваться добавить тестовые зависимости, связанные с ними.

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.0'

Полную кодовую базу можно найти в репозитории Github android_unit_tests. Как только вы запустите LoginViewModelTest, мы увидим, что все тесты пройдены.

Шаг 2: Написание тестов для LoginRepo

LoginRepo — это не что иное, как интерфейс с абстрактным методом validateLoginDetails.

package com.sample

interface LoginRepo {
     fun validateLoginDetails(username: String, pass: String): LoginResponse?
}

Давайте создадим класс LoginRepoTest и проведем аналогичное тестирование, которое мы сделали выше. Поскольку у нас есть только один метод в репо, у нас есть два возможных результата для него: успешное и неудачное состояние. Код LoginRepoTest приведен ниже:

Запуск LoginRepoTest приведет к успешному прохождению тестов.

Шаг 3: Написание тестов для LoginAPIService

Давайте посмотрим на LoginAPIService.

Чтобы протестировать APIService, нам нужно добавить фиктивную зависимость веб-сервера в файл Gradle на уровне модуля:

testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'

Нам нужно создать экземпляр MockWebServer и использовать его с Retrofit для получения ожидаемых результатов. Здесь мы устанавливаем данные ответа веб-серверу, которые будут возвращаться при вызовах методов. Давайте посмотрим, как это сделать:

Здесь мы обработали состояние успеха, теперь попробуйте записать состояние отказа и проверьте его, запустив.

Если у вас возникли проблемы при выполнении сниппетов, ознакомьтесь с полной кодовой базой в репозитории Github android_unit_tests.

Краткое содержание

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

Спасибо за прочтение…

Рекомендации