Осваиваем разработку через тестирование в Android с помощью UI-тестов
Всем привет. В преддверии старта нового набора на базовый и продвинутый курсы по Android разработке подготовили перевод интересного материала.
За последний год работы команды Android разработки в Buffer мы много говорили о чистоте нашего проекта и повышении его стабильности. Одним из факторов было введение () тестов, которые, как мы уже выяснили, помогают нам избежать регрессий в нашем коде и дают нам большую уверенность в предоставляемых нами фичах. И теперь, когда мы запускаем новые продукты в Buffer, мы хотим убедиться, что применяется тот же подход, когда дело доходит и до них — просто чтобы мы не оказались в той же ситуации, что и раньше.
Предпосылки
При написании модульных тестов для приложений Buffer мы всегда придерживались практики разработки через тестирование (Test-Driven-Development — далее TDD). Существует много ресурсов о том, что такое TDD, о его преимуществах, и откуда они берутся, но мы не будем останавливаться на этой теме, так как в Интернете достаточно много информации. На высоком уровне лично я отмечу некоторые из них:
- Сокращено время разработки
- Более простой, понятный и поддерживаемый код
- Более надежный код с большей уверенностью в нашей работе
- Более высокий тестовый охват (это вроде как очевидно )
Но до недавнего времени мы только следовали принципам TDD только в форме модульных тестов для наших реализаций, не основанных на пользовательском интерфейсе…
Я знаю, я знаю… Мы всегда имели привычку писать UI-тесты уже после того, как то, что было реализовано, завершено — и это не имеет смысла. Мы следили за TDD для бэкэнда, чтобы код, который мы пишем, соответствовал требованиям, которые определяют тесты, но когда речь идет о тестах пользовательского интерфейса, мы пишем тесты, которые удовлетворяют реализации конкретной фичи. Как вы можете видеть, это немного противоречиво, и в некотором роде речь идет о том, почему TDD используется в первую очередь.
Итак, здесь я хочу рассмотреть, почему это так, и как мы экспериментируем с изменениями. Но почему мы вообще уделяем этому внимание в первую очередь?
С существующими activity в нашем приложении всегда было трудно работать из-за того, как они написаны. Это не в полной мере оправдание, но многочисленные зависимости, обязанности и тесная связь делают их чрезвычайно трудными для тестирования. Для новых activity, которые мы добавляли, по привычке я сам всегда писал UI-тесты после реализации — кроме привычки, другой причины для этого не было. Тем не менее, при создании нашего шаблонного кода, готового к новым проектам, я задумался об изменении. И вы будете рады узнать, что эта привычка была сломлена, и теперь мы работаем над собой, исследуя TDD для UI-тестов
Первые шаги
То, что мы собираемся исследовать здесь, является довольно простым примером, для того, чтобы концепцию было легче проследить и понять — надеюсь, этого будет достаточно, чтобы увидеть некоторые преимущества этого подхода.
Мы собираемся начать с создания базового activity. Нам нужно сделать это, чтобы мы могли запустить наш UI-тест — представьте, что этот сетап является основой для нашей реализации, а не самой реализацией. Вот как выглядит наше базовое activity:
Вы можете заметить, что это activity не выполняет ничего, кроме начального сетапа, который требуется для activity. В методе onCreate() мы просто устанавливаем ссылку на layout, у нас также есть ссылка на наш View интерфейс, который реализован с помощью activity, но у них еще нет реализаций.
Одна из самых распространенных вещей, которые мы встречаем в Espresso тестах, — это ссылочные view и строки по ID ресурсов, найденных в нашем приложении. В связи с этим нам снова необходимо предоставить файл layout для использования в нашем activity. Это происходит из-за того, что: а) нашему activity нужен файл layout для отображения layout-а во время тестов, и б) нам нужны ID view для ссылок в наших тестах. Давайте продолжим и сделаем очень простой layout для нашего activity входа в систему:
Здесь вы можете заметить, что мы не беспокоились по поводу какого-либо стиля или позиции, помните — пока мы создаем фундамент, а не реализацию.
И для последней части сетапа мы собираемся определить строки, которые будут использоваться в этом упражнении. Опять же, нам нужно будет ссылаться на них в тестах — пока не добавляйте их в свой XML layout или класс activity, просто определите их в файле strings.xml .
Вы можете заметить, что в этом сетапе мы пишем как можно меньше, но предоставляем достаточно деталей о нашем activity и ее layout, чтобы написать для него тесты. Наше activity на данный момент не работает, но оно открывается и имеет view, на которые можно ссылаться. Теперь, когда мы имеем достаточный минимум для работы, давайте продолжим и добавим несколько тестов.
Добавление тестов
Итак, у нас есть три ситуации, которые мы должны реализовать, поэтому мы собираемся написать несколько тестов для них.
- Когда пользователь вводит недействительный адрес электронной почты в поле ввода электронной почты, нам нужно отобразить сообщение об ошибке. Итак, мы собираемся написать тест, который проверяет, отображается ли это сообщение об ошибке.
- Когда пользователь снова начинает вводить данные в поле ввода электронной почты, приведенное выше сообщение об ошибке должно исчезнуть — поэтому мы собираемся написать для этого тест.
- Наконец, когда API возвращает сообщение об ошибке, оно должно отображаться в диалоговом окне с предупреждением — поэтому мы также добавим тест для этого.
Отлично, теперь у нас есть написанные тесты — давайте продолжим и запустим их.
И неудивительно, что они потерпели неудачу — это потому, что у нас еще нет реализации, так что этого следовало ожидать Во всяком случае, мы должны быть рады видеть красный для тестов именно сейчас!
Итак, теперь нам нужно добавить реализации для нашего activity, пока тесты не будут пройдены. Поскольку мы пишем сфокусированные тесты, которые тестируют только одну концепцию (или, по крайней мере, так должно быть!), Мы сможем добавлять реализации одну за другой, а также смотреть, как наши тесты становятся зелеными один за другим.
Итак, давайте посмотрим на один из проваленных тестов, начнем с теста invalidPasswordErrorDisplayed(). Мы знаем несколько вещей:
- Чтобы запустить процесс входа в систему, пользователь вводит свой пароль, а затем нажимает кнопку входа в систему, поэтому нам нужно реализовать прослушиватель для кнопки входа в систему, который вызывает наш метод входа для докладчика:
- Когда пользователь не вводит пароль в поле пароля, нам нужно реализовать логику для отображения этого сообщения об ошибке. Мы используем компонент TextInputLayout, поэтому мы можем просто присвоить значение его сообщения об ошибке нашей строке ошибки, которую мы определили ранее:
Теперь мы добавили логику для этой ситуации, давайте продолжим и снова запустим наши тесты!
Отлично, похоже, что проверка invalidPassworrdErrorDisplays() прошла успешно. Но мы еще не закончили, у нас все еще есть два теста, которые не пройдены для тех частей нашей функции входа, которые мы должны реализовать.
Далее мы рассмотрим тест serverErrorMessageDisplays() . Это довольно просто, мы знаем, что когда API возвращает ответ об ошибке (а не общую ошибку из нашей сетевой библиотеки), приложение должно показать сообщение об ошибке пользователю в диалоговом окне с предупреждением. Для этого нам просто нужно создать экземпляр диалога, используя наше сообщение об ошибке сервера в тексте диалога:
Давайте продолжим и снова запустим наши тесты:
Ура! Мы продвигаемся, теперь у нас остался только один тест, это тест invalidEmailErrorHidesWhenUserTypes() . Опять же, это простой случай, но давайте разберем его:
- Когда пользователь нажимает кнопку входа в систему и отсутствует введенный адрес электронной почты или указан неверный адрес электронной почты, мы показываем пользователю сообщение об ошибке. Мы уже реализовали это, я просто исключил это для простоты
- Однако, когда пользователь снова начинает вводить данные в поле, сообщение об ошибке должно быть удалено из отображения. Для этого нам нужно слушать, когда изменяется текстовое содержимое поля ввода:
Теперь этого должно быть достаточно, чтобы гарантировать, что наше сообщение об ошибке будет скрыто при изменении содержимого поля ввода. Но у нас есть тесты, чтобы подтвердить наши изменения:
Отлично! Наши требования к реализации выполняются по мере прохождения наших тестов — здорово видеть «зеленый свет»
Заключение
Важно отметить, что пример, к которому мы применили TDD, чрезвычайно примитивный. Представьте, что мы разрабатываем сложный экран, такой как контент фид, в котором можно выполнять несколько действий с элементами фида (например, как в приложении Buffer дляr Android) — в этих случаях мы будем использовать множество различных функций, которые должны быть реализованы в заданных activity/фрагменте. Это ситуации, когда TDD в UI-тестах будет раскрываться еще больше, поскольку то, что может привести к тому, что мы будем писать слишком сложный код для этих функций, может быть сведено к реализациям, которые удовлетворяют заданным тестам, которые мы написали.
Чтобы подытожить, я поделюсь некоторыми моментами, извлеченными из моего опыта:
- Я иногда замечал, что люди говорят, что TDD замедляет разработку. Я не чувствовал, что это имело здесь место по нескольким причинам. Начнем с того, что Espresso написан на беглом языке (представление/текст, за которым следует ожидаемое состояние), поэтому для написания этих тестов требуется совсем немного времени. Когда дело дошло до написания логики activity, я чувствовал, что мои требования были четко изложены, и мои тесты были там для проверки поведения. Это исключает случай написания тестов для удовлетворения кода и собственно написания тестов на основе требований реализации.
- В свою очередь, этот момент означает, что в более сложных реализациях, чем в примере, мы, скорее всего, напишем меньше кода, чем если бы мы писали тесты после. Это потому, что мы пишем код для удовлетворения наших тестов, поэтому, как только наши тесты будут пройдены, это означает, что наши реализации достаточно хороши (при условии, что мы пишем наши тесты правильно!). Из-за этого важно писать небольшие и целенаправленные тесты пользовательского интерфейса. Как только мы начинаем группировать несколько тестовых случаев в отдельные тесты, тогда, вероятно, мы что-то упустим.
- Я чувствовал, что написание Ui-тестов в первую очередь дало мне еще лучшее и более ясное понимание требований к тому, что я реализовывал, что не всегда было бы в силе в противном случае. Это, в свою очередь, вероятно, приведет к тому, что процесс разработки для реализации будет короче, в отличие от мнения других коллег, упомянутых в первом пункте.
- Подход гарантирует, что мы пишем полный набор тестов. Тесты не будут забыты или не реализованы, так как наши требования сформулированы нашими тестами — так как нам нужно реализовать то, что тесты пишутся в первую очередь, что делает довольно трудным пропустить некоторые из них, или пренебречь ими по какой-либо причине.
- Это кажется более естественным. Из-за того, что TDD уже используется для модульных тестов, вы чувствуете себя немного задом наперед, когда пишет модульные тесты, за которыми следуют реализации, за которыми UI-тесты. Вы почувствуете себя более естественно, идя полным шагом с TDD, а не наполовину.
Вы уже используете TDD при написании тестов пользовательского интерфейса и делаете что-то похожее или совершенно другое? Или вы хотите узнать немного больше и задать несколько вопросов? Не стесняйтесь комментировать ниже или напишите нам твит на @bufferdevs
Источник
Как создать тесты в Android Studio?
Только что загрузили Android Studio, основанную на Intellij Idea.
Как создать тесты?
Я заметил, что есть опция для создания тестового модуля, но это ничего не делает, только создает новый проект с помощью src
Я также попытался нажать горячую клавишу CTRL + AlT + T, которая позволяет создавать модульные тесты для существующего класса, но, кажется, он хочет поместить его в текущий проект. Конечно, это не помогает с TDD
У кого-нибудь есть опыт здесь?
Этот ответ предназначен для тех, кто только начинает тестирование Android. Я приведу два простых примера, которые помогут вам понять, как работает тестирование. Если вы будете следовать в течение следующих 10 минут, у вас будет все готово, чтобы начать добавлять свои тесты в собственное приложение. Я думаю, вы удивитесь, насколько это просто. Я конечно был.
Введение в Android Тестирование
Есть два разных типа тестов, которые вы будете делать.
- Локальные модульные тесты. Они выполняются локально на JVM (виртуальная машина Java). Так как они местные, они быстрые. Вы можете использовать их для тестирования частей вашего кода, которые просто нуждаются в Java, а не в Android API. (Иногда вы можете создать поддельный объект API для локального тестирования большего количества вещей. Это называется насмешливый . Примером Context является пример.)
- Инструментированные тесты. Эти тесты выполняются на реальном устройстве или в эмуляторе. Это делает их медленнее, чем местные тесты. Однако они более гибкие, поскольку у вас есть полный доступ к API Android.
Создайте новый проект, и вы увидите следующие папки по умолчанию.
Все уже есть и ждут, когда вы создадите свои тесты. Все уже настроено!
Как создать локальные юнит-тесты
Откройте файл ExampleUnitTest , показанный на изображении выше. это должно выглядеть примерно так:
Нажмите двойную зеленую стрелку, чтобы запустить все тесты, или одну зеленую стрелку, чтобы запустить только один. (В этом случае есть только один тест, поэтому они оба делают одно и то же.)
Это должно пройти (пока 2 + 2 все еще 4 , когда вы читаете этот ответ). Поздравляем, вы только что провели первый тест!
Создание собственного теста
Давайте напишем наш собственный тест. Сначала добавьте этот класс в основной проект приложения, чтобы у нас было что проверить:
Теперь измените метод addition_isCorrect() в тестовом классе так, чтобы он был похож на следующий код (или просто добавьте другой метод с другим именем):
Запустите его снова, и вы должны увидеть, как он прошел. Поздравляем, вы только что создали свой первый тест! (Ну, я думаю, технически это было мое, но, эй, достаточно близко. Что мое, то ваше.)
Как создавать инструментальные тесты
Откройте файл ExampleInstrumentedTest . это должно выглядеть примерно так:
Нажмите одну из этих зеленых кнопок еще раз.
Если у вас подключено реальное устройство или настроен эмулятор, он должен был запустить его и запустить ваше приложение. Поздравляем, вы только что провели первый инструментальный тест!
Создание собственного теста
Инструментированные тесты используют Espresso для запуска тестов. Это своего рода маленький пользователь-робот, который может протестировать ваше приложение. Вы можете сказать ему сделать что-то вроде нажатия кнопки или чтения свойств TextView.
Вы можете написать инструкции о том, как выполнить тест вручную, но так как мы только начинаем, давайте использовать функция автоматической записи . Это супер просто.
Сначала добавьте кнопку в свой интерфейс, чтобы у нас было с чем работать. Я сделал это:
Затем нажмите Run> Record Espresso Test в меню.
После того, как он запустится, нажмите кнопку в эмуляторе, а затем, чтобы закончить, выберите OK в диалоговом окне записи. Он должен автоматически сгенерировать следующий тестовый код.
Большой! Вы только что создали первый инструментальный тест! Это было супер легко. Вы, вероятно, должны добавить утверждение, чтобы сделать его настоящим тестом, но это также довольно легко сделать с рекордером. Смотрите это видео , чтобы пойти немного глубже.
Дальнейшее обучение
Сначала я смотрел видео, а потом читал документацию. Это все очень полезно. Последняя ссылка — на серию статей, в которых рассматриваются некоторые важные вещи, которые следует учитывать при выборе того, что тестировать.
Правка: Начиная с .1.8 теперь поддерживается в IDE . Пожалуйста, следуйте инструкциям там, вместо того, чтобы использовать инструкции ниже.
Следуя Руководство пользователя плагина Android Gradle Я смог получить тесты, работающие в командной строке, выполнив следующие шаги для вновь созданного проекта (я использовал стандартный пакет ‘com.example.myapplication’):
- Добавить каталог src/instrumentTest/Java для тестов
- Добавьте тестовый класс (расширяющий ActivityTestCase) в пакет com.example.myapplication.test
- Запустите виртуальное устройство
- В командной строке (в каталоге MyApplicationProject/MyApplication) используйте команду ‘../gradlew connectedInstrumentTest’
Это запустило мои тесты и поместило результаты теста в MyApplicationProject/MyApplication/build/reports/instrumentTests/connected. Я новичок в тестировании Android приложений, но, похоже, работает нормально.
Из среды IDE можно попробовать запустить тот же тестовый класс. Вам нужно будет
- Обновите build.gradle, чтобы указать Maven Central в качестве репозитория
- Обновите build.gradle, добавьте JUnit 3.8 в качестве зависимости toolTestCompile, например, toolTestCompile ‘junit: junit: 3.8’
- В «Структуре проекта» вручную переместите JUnit, чтобы быть первым в порядке зависимости
Однако это терпит неудачу (путь к классу, используемый при выполнении тестов, отсутствует в каталоге выходных данных теста). Однако я не уверен, что это сработает, независимо от того, насколько я понимаю, что требуется специальный тестировщик Android.
Я бы предложил использовать файл gradle.build.
Добавьте каталог src/androidTest/Java для тестов (Как Крис начинает объяснять)
Откройте файл gradle.build и укажите там:
Нажмите «Синхронизировать проект с файлом Gradle» (на верхней панели). Теперь вы должны увидеть папку «Java» (внутри «androidTest») зеленого цвета.
Теперь вы можете создавать там любые тестовые файлы и выполнять их.
Я думаю этот пост от Rex St John очень полезен для модульного тестирования в студии Android.
Android Studio v.2.3.3
Выделите контекст кода, который вы хотите протестировать, и используйте горячую клавишу: CTRL + SHIFT + T
Используйте диалоговый интерфейс, чтобы завершить настройку.
Предполагается, что инфраструктура тестирования будет отражать макет пакета вашего проекта для достижения наилучших результатов, но вы можете вручную создавать собственные тесты, если у вас есть правильные каталог и настройки сборки.
На данный момент (студия 0.61) достаточно поддерживать правильную структуру проекта. Нет необходимости создавать отдельный тестовый проект, как в Eclipse (см. Ниже).
Android Studio продолжает развиваться, поэтому приведенные выше ответы в конечном итоге перестанут применяться. Для текущей версии Android Studio 1.2.1.1 есть хорошее руководство по тестированию:
Похоже, одно из основных изменений заключается в том, что в Android Studio тестовое приложение интегрировано в проект приложения.
Я не уверен, поможет ли это вашей конкретной проблеме, но я нашел руководство по созданию тестов с проектом Gradle. Руководство пользователя Android Gradle
Самый простой способ, который я нашел, — это упорядоченный в мой следующий пост в блоге :
- Создайте папку, в которую вы будете писать все свои юнит-тесты (предпочтительно com.example.app.tests)
- Создайте новый тестовый класс (предпочтительно NameOfClassTestedTests, т.е. BankAccountLoginActivityTests)
- Расширить InstrumentationTestCase
- Напишите провальный модульный тест, чтобы убедиться, что мы успешно настроили модульные тесты
- Обратите внимание, что имя метода модульного теста должно начинаться со слова «test» (предпочтительно testTestedMethodNameExpectedResult (), т.е. testBankAccountValidationFailedShouldLogout ())
- Сконфигурируйте ваш проект для модульных тестов:
- Откройте меню «Выполнить . » и нажмите «Изменить настройки»
- Нажмите кнопку +
- Выберите шаблон Android Тесты
- Введите имя для своей конфигурации запуска (предпочтительно «Тесты AppName»)
- Выберите ваше приложение в модуле выпадающего списка
- Выберите переключатель «Все в пакете» (обычно вы хотите выбрать этот параметр, поскольку он запускает все модульные тесты во всех ваших тестовых классах).
- Введите имя тестового пакета, начиная с шага 1 (например, com.example.app.tests).
- Выберите устройство, на котором вы хотите запустить свои тесты
- Применить и сохранить конфигурацию
- Запустите юнит-тесты (и ожидайте сбой):
- Выберите вновь созданную конфигурацию тестов в меню «Выполнить»
- Нажмите Run и прочитайте результаты в консоли вывода.
Удачи в том, чтобы сделать ваш код более читабельным, поддерживаемым и хорошо протестированным!
Android Studio была своего рода движущейся целью: сначала она представляла собой предварительную версию для разработчиков, а сейчас находится в бета-версии. Путь к классам Test в проекте менялся с течением времени, но независимо от того, какую версию AS вы используете, путь объявляется в вашем файле .iml. В настоящее время с версией 0.8.3 вы найдете следующее во внутреннем IML-файле:
Файл .iml сообщает вам, где разместить тестовые классы.
Начиная с Android Studio 1.1, у нас есть официальная (экспериментальная) поддержка написания модульных тестов (Roboelectric работает также).
Добавьте ниже lib внутри файла gradle
Создайте класс HomeActivityTest в каталоге androidTest и перед запуском теста добавьте строку flurry_api_key и sender_id в строковый файл ресурсов и измените значение для случая неудачи и успеха.
Источник