На этой странице изложены основные принципы тестирования приложений Android, включая основные рекомендации и их преимущества.
Преимущества тестирования
Тестирование — неотъемлемая часть процесса разработки приложения. Последовательно выполняя тесты вашего приложения, вы можете проверить его корректность, функциональное поведение и удобство использования, прежде чем выпустить его публично.
Вы можете вручную протестировать свое приложение, просматривая его. Вы можете использовать разные устройства и эмуляторы, менять язык системы и пытаться сгенерировать каждую ошибку пользователя или пройти через каждый пользовательский поток.
Однако ручное тестирование плохо масштабируется, и можно легко не заметить регрессии в поведении вашего приложения. Автоматизированное тестирование предполагает использование инструментов, которые выполняют тесты за вас. Это быстрее, более воспроизводимо и, как правило, дает вам более полезную обратную связь о вашем приложении на ранних этапах процесса разработки.
Виды тестов в Android
Мобильные приложения сложны и должны хорошо работать во многих средах. Таким образом, существует множество типов тестов.
Предмет
Например, в зависимости от предмета существуют разные типы тестов:
- Функциональное тестирование : делает ли мое приложение то, что должно?
- Тестирование производительности : делается ли оно быстро и эффективно?
- Тестирование доступности : хорошо ли оно работает со службами доступности?
- Тестирование совместимости : хорошо ли оно работает на каждом устройстве и уровне API?
Объем
Тесты также различаются в зависимости от размера или степени изоляции :
- Модульные тесты или небольшие тесты проверяют только очень небольшую часть приложения, например метод или класс.
- Сквозные тесты или большие тесты проверяют одновременно большие части приложения, например весь экран или пользовательский поток.
- Средние тесты находятся между ними и проверяют интеграцию между двумя или более модулями.
Существует множество способов классификации тестов. Однако наиболее важным отличием для разработчиков приложений является место проведения тестов.
Инструментальные и локальные тесты
Вы можете запускать тесты на Android-устройстве или на другом компьютере:
- Инструментальные тесты выполняются на устройстве Android, физическом или эмулируемом. Приложение создается и устанавливается вместе с тестовым приложением , которое вводит команды и считывает состояние. Инструментированные тесты обычно представляют собой тесты пользовательского интерфейса, запускающие приложение и последующее взаимодействие с ним.
- Локальные тесты выполняются на вашей машине разработки или сервере, поэтому их также называют тестами на стороне хоста . Обычно они небольшие и быстрые и изолируют тестируемый объект от остальной части приложения.
Не все модульные тесты являются локальными, и не все сквозные тесты выполняются на устройстве. Например:
- Большой локальный тест . Вы можете использовать симулятор Android, который работает локально, например Robolectric .
- Небольшой инструментальный тест . Вы можете убедиться, что ваш код хорошо работает с какой-либо функцией платформы, например базой данных SQLite. Вы можете запустить этот тест на нескольких устройствах, чтобы проверить интеграцию с несколькими версиями SQLite.
Примеры
Следующие фрагменты демонстрируют, как взаимодействовать с пользовательским интерфейсом в инструментальном тесте пользовательского интерфейса , который щелкает элемент и проверяет, отображается ли другой элемент.
Эспрессо
// When the Continue button is clicked
onView(withText("Continue"))
.perform(click())
// Then the Welcome screen is displayed
onView(withText("Welcome"))
.check(matches(isDisplayed()))
Создать интерфейс
// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()
// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()
В этом фрагменте показана часть модульного теста для ViewModel (локальный тест на стороне хоста):
// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)
// When data is loaded
viewModel.loadData()
// Then it should be exposing data
assertTrue(viewModel.data != null)
Определение стратегии тестирования
В идеальном мире вы должны тестировать каждую строку кода вашего приложения на каждом устройстве, с которым оно совместимо. К сожалению, этот подход слишком медленный и дорогостоящий, чтобы быть практичным.
Хорошая стратегия тестирования находит подходящий баланс между точностью теста, его скоростью и надежностью. Сходство тестовой среды с реальным устройством определяет точность теста. Тесты более высокой точности выполняются на эмулируемых устройствах или на самом физическом устройстве. Тесты с более низкой точностью могут выполняться на JVM вашей локальной рабочей станции. Тесты высокой точности часто выполняются медленнее и требуют больше ресурсов, поэтому не каждый тест должен быть тестом высокой точности.
Ненадежные тесты
Ошибки случаются даже при правильно спроектированных и реализованных тестовых запусках. Например, при запуске теста на реальном устройстве автоматическое обновление может начаться в середине теста и привести к его сбою. Незаметные состояния гонки в вашем коде могут возникать лишь в небольшом проценте случаев. Тесты, которые не проходят в 100% случаев, нестабильны .
Тестируемая архитектура
Благодаря тестируемой архитектуре приложения код имеет структуру, которая позволяет легко тестировать различные его части по отдельности. Тестируемые архитектуры имеют и другие преимущества, такие как лучшая читаемость, удобство сопровождения, масштабируемость и возможность повторного использования.
Архитектура, которая не поддается тестированию, приводит к следующему:
- Более масштабные, медленные и нестабильные тесты. Классы, которые не могут быть подвергнуты модульному тестированию, возможно, придется охватить более крупными интеграционными тестами или тестами пользовательского интерфейса.
- Меньше возможностей для тестирования разных сценариев. Большие тесты выполняются медленнее, поэтому тестирование всех возможных состояний приложения может оказаться нереальным.
Дополнительные сведения о рекомендациях по архитектуре см. в руководстве по архитектуре приложений .
Подходы к развязке
Если вы можете извлечь часть функции, класса или модуля из остальных, тестирование будет проще и эффективнее. Эта практика известна как развязка, и это концепция, наиболее важная для тестируемой архитектуры.
Общие методы развязки включают в себя следующее:
- Разделите приложение на слои, такие как «Презентация», «Домен» и «Данные». Вы также можете разделить приложение на модули , по одному на каждую функцию.
- Избегайте добавления логики к сущностям, имеющим большие зависимости, таким как действия и фрагменты. Используйте эти классы в качестве точек входа в платформу и перемещайте пользовательский интерфейс и бизнес-логику в другое место, например, на составной уровень, ViewModel или уровень домена.
- Избегайте прямых зависимостей платформы в классах, содержащих бизнес-логику. Например, не используйте контексты Android в ViewModels .
- Сделайте зависимости легко заменяемыми . Например, используйте интерфейсы вместо конкретных реализаций. Используйте внедрение зависимостей, даже если вы не используете платформу DI.
Следующие шаги
Теперь, когда вы знаете, почему вам следует тестировать и какие два основных типа тестов вы знаете, вы можете прочитать «Что тестировать» .
Альтернативно, если вы хотите создать свой первый тест и учиться на практике, ознакомьтесь с лабораторными работами по тестированию .