Создание локальных модульных тестов

Локальный тест выполняется непосредственно на вашей рабочей станции, а не на устройстве Android или в эмуляторе. Таким образом, для запуска тестов он использует вашу локальную виртуальную машину Java (JVM), а не устройство Android. Локальные тесты позволяют быстрее оценить логику вашего приложения. Однако отсутствие возможности взаимодействия с платформой Android создает ограничение на типы тестов, которые вы можете запускать.

Модульный тест проверяет поведение небольшого участка кода — тестируемого модуля . Он делает это, выполняя этот код и проверяя результат.

Модульные тесты обычно просты, но их настройка может быть проблематичной, если тестируемый модуль не спроектирован с учетом тестируемости:

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

Чтобы узнать об распространенных стратегиях модульного тестирования в Android, прочтите «Что тестировать» .

Место проведения локальных испытаний

По умолчанию исходные файлы для локальных модульных тестов помещаются в module-name/src/test/ . Этот каталог уже существует, когда вы создаете новый проект с помощью Android Studio.

Добавление зависимостей тестирования

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

Для этого откройте файл build.gradle модуля вашего приложения и укажите следующие библиотеки в качестве зависимостей. Используйте функцию testImplementation чтобы указать, что они применяются к локальному набору исходных текстов тестов, а не к приложению:

dependencies {
  // Required -- JUnit 4 framework
  testImplementation "junit:junit:$jUnitVersion"
  // Optional -- Robolectric environment
  testImplementation "androidx.test:core:$androidXTestVersion"
  // Optional -- Mockito framework
  testImplementation "org.mockito:mockito-core:$mockitoVersion"
  // Optional -- mockito-kotlin
  testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
  // Optional -- Mockk framework
  testImplementation "io.mockk:mockk:$mockkVersion"
}

Создайте локальный класс модульного теста.

Вы пишете свой локальный класс модульного теста как тестовый класс JUnit 4 .

Для этого создайте класс, содержащий один или несколько методов тестирования, обычно в module-name/src/test/ . Метод тестирования начинается с аннотации @Test и содержит код для тестирования и проверки отдельного аспекта компонента, который вы хотите протестировать.

В следующем примере показано, как реализовать класс локального модульного теста. Тестовый метод emailValidator_correctEmailSimple_returnsTrue() пытается проверить isValidEmail() , который является методом внутри приложения. Тестовая функция вернет true, если isValidEmail() также вернет true.

Котлин

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class EmailValidatorTest {
  @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"))
  }

}

Ява

import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

class EmailValidatorTest {
  @Test
  public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"));
  }
}

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

Мокабельная библиотека Android

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

Это позволяет создавать локальные тесты при ссылке на классы в платформе Android, такие как Context . Что еще более важно, он позволяет использовать макетную структуру с классами Android.

Издевательство над зависимостями Android

Типичная проблема — обнаружить, что класс использует строковый ресурс. Вы можете получить строковые ресурсы, вызвав метод getString() в классе Context . Однако локальный тест не может использовать Context или любой из его методов, поскольку они принадлежат платформе Android. В идеале вызов getString() должен быть вынесен из класса, но это не всегда практично. Решение состоит в том, чтобы создать макет или заглушку Context , который всегда возвращает одно и то же значение при вызове метода getString() .

С помощью библиотеки Mockable Android и фреймворков для макетов, таких как Mockito или MockK , вы можете программировать поведение макетов классов Android в своих модульных тестах.

Чтобы добавить макет объекта в локальный модульный тест с помощью Mockito, следуйте этой модели программирования:

  1. Включите зависимость библиотеки Mockito в файл build.gradle , как описано в разделе Настройка среды тестирования .
  2. В начале определения класса модульного теста добавьте аннотацию @RunWith(MockitoJUnitRunner.class) . Эта аннотация сообщает средству запуска тестов Mockito, что нужно проверить правильность использования вами платформы, и упрощает инициализацию ваших фиктивных объектов.
  3. Чтобы создать макет объекта для зависимости Android, добавьте аннотацию @Mock перед объявлением поля.
  4. Чтобы заглушить поведение зависимости, вы можете указать условие и возвращаемое значение, когда условие выполняется, используя методы when() и thenReturn() .

В следующем примере показано, как можно создать модульный тест, использующий макет объекта Context в Kotlin, созданный с помощью Mockito-Kotlin .

import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

private const val FAKE_STRING = "HELLO WORLD"

@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {

  @Mock
  private lateinit var mockContext: Context

  @Test
  fun readStringFromContext_LocalizedString() {
    // Given a mocked Context injected into the object under test...
    val mockContext = mock<Context> {
        on { getString(R.string.name_label) } doReturn FAKE_STRING
    }

    val myObjectUnderTest = ClassUnderTest(mockContext)

    // ...when the string is returned from the object under test...
    val result: String = myObjectUnderTest.getName()

    // ...then the result should be the expected one.
    assertEquals(result, FAKE_STRING)
  }
}

Дополнительные сведения об использовании платформы Mockito см. в справочнике по API Mockito и классе SharedPreferencesHelperTest в примере кода . Также попробуйте Android Testing Codelab .

Ошибка: «Метод... не осмеян»

Библиотека Mockable Android выдает исключение, если вы пытаетесь получить доступ к любому из ее методов с Error: "Method ... not mocked .

Если выдаваемые исключения создают проблемы для ваших тестов, вы можете изменить поведение так, чтобы вместо этого методы возвращали либо значение NULL, либо ноль, в зависимости от типа возвращаемого значения. Для этого добавьте следующую конфигурацию в файл build.gradle верхнего уровня вашего проекта в Groovy:

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }