Локальный тест выполняется непосредственно на вашей рабочей станции, а не на устройстве 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 и содержит код для проверки одного аспекта тестируемого компонента.
В следующем примере показано, как реализовать класс локального модульного тестирования. Метод test emailValidator_correctEmailSimple_returnsTrue()пытается проверить isValidEmail(), который является методом в приложении. Функция test вернет true, если isValidEmail() также вернет true.
Kotlin
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")) } }
Java
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.
Библиотека Mockable Android
При выполнении локальных модульных тестов плагин Android Gradle включает библиотеку, которая содержит все API фреймворка Android, соответствующие версии, используемой в вашем проекте. В библиотеке содержатся все общедоступные методы и классы этих API, но код внутри методов удален. Если к какому-либо из методов осуществляется доступ, тест выдает исключение.
Это позволяет создавать локальные тесты при ссылке на классы в фреймворке Android, например Context. Но самое главное – это позволяет использовать фреймворк для имитации с классами Android.
Имитация зависимостей Android
Типичная проблема заключается в том, что класс использует строковый ресурс. Строковые ресурсы можно получить, вызвав метод getString() в классе Context. Однако в локальном тесте нельзя использовать Context или любой из его методов, поскольку они принадлежат фреймворку Android. В идеале вызов getString() нужно вынести за пределы класса, но это не всегда возможно. Решение состоит в том, чтобы создать макет или заглушку Context, которая всегда возвращает одно и то же значение при вызове метода getString().
С помощью библиотеки Mockable Android и фреймворков, таких как Mockito или MockK, вы можете программировать поведение макетов классов Android в модульных тестах.
Чтобы добавить макет объекта в локальное модульное тестирование с помощью Mockito, используйте следующую модель программирования:
- Добавьте зависимость библиотеки Mockito в файл
build.gradle, как описано в разделе Настройка тестовой среды. - В начале определения класса модульного тестирования добавьте аннотацию
@RunWith(MockitoJUnitRunner.class). Эта аннотация сообщает исполнителю тестов Mockito, что нужно проверить правильность использования фреймворка, и упрощает инициализацию макетных объектов. - Чтобы создать макет объекта для зависимости Android, добавьте аннотацию
@Mockперед объявлением поля. - Чтобы имитировать поведение зависимости, вы можете указать условие и возвращаемое значение, когда условие выполняется, с помощью методов
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 можно найти в документации по Mockito API и в классе SharedPreferencesHelperTest в образце кода. Также рекомендуем ознакомиться с практической работой по тестированию на Android.
Ошибка "Method ... not mocked"
Библиотека Mockable Android выдает исключение, если вы пытаетесь получить доступ к любому из ее методов с сообщением Error: "Method ... not mocked.
Если исключения мешают тестированию, вы можете изменить поведение методов, чтобы они возвращали null или ноль в зависимости от типа возвращаемого значения. Для этого добавьте следующую конфигурацию в файл build.gradle верхнего уровня проекта в Groovy:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}