Учебники

Espresso Testing Framework — Краткое руководство

Espresso Testing Framework — Введение

В целом, тестирование мобильной автоматизации является сложной и сложной задачей. Доступность Android для различных устройств и платформ делает его утомительным для тестирования мобильной автоматизации. Чтобы сделать это проще, Google взял на себя задачу и разработал каркас Espresso. Он предоставляет очень простой, согласованный и гибкий API для автоматизации и тестирования пользовательских интерфейсов в приложениях для Android. Тесты эспрессо могут быть написаны на Java и Kotlin, современном языке программирования для разработки приложений для Android.

API-интерфейс Espresso прост и легок в освоении. Вы можете легко выполнять тестирование пользовательского интерфейса Android без сложности многопоточного тестирования. Google Drive, Карты и некоторые другие приложения в настоящее время используют Espresso.

Особенности эспрессо

Вот некоторые характерные особенности, поддерживаемые Espresso:

  • Очень простой API и так легко учиться.

  • Высоко масштабируемый и гибкий.

  • Предоставляет отдельный модуль для тестирования компонента Android WebView.

  • Предоставляет отдельный модуль для проверки, а также макет Android-содержимого.

  • Обеспечивает автоматическую синхронизацию между вашим приложением и тестами.

Очень простой API и так легко учиться.

Высоко масштабируемый и гибкий.

Предоставляет отдельный модуль для тестирования компонента Android WebView.

Предоставляет отдельный модуль для проверки, а также макет Android-содержимого.

Обеспечивает автоматическую синхронизацию между вашим приложением и тестами.

Преимущества эспрессо

Давайте теперь рассмотрим преимущества эспрессо.

  • Обратная совместимость

  • Прост в настройке.

  • Высокостабильный цикл испытаний.

  • Поддерживает тестирование вне приложения.

  • Поддерживает JUnit4

  • Автоматизация пользовательского интерфейса подходит для написания тестов черного ящика.

Обратная совместимость

Прост в настройке.

Высокостабильный цикл испытаний.

Поддерживает тестирование вне приложения.

Поддерживает JUnit4

Автоматизация пользовательского интерфейса подходит для написания тестов черного ящика.

Espresso Testing Framework — Инструкции по настройке

В этой главе мы расскажем, как установить эспрессо-фреймворк, настроить его для написания эспрессо-тестов и выполнить его в нашем приложении для Android.

Предпосылки

Espresso — это среда тестирования пользовательского интерфейса для тестирования приложений Android, разработанная на языке Java / Kotlin с использованием Android SDK. Поэтому единственным требованием к эспрессо является разработка приложения с использованием Android SDK на Java или Kotlin, и рекомендуется использовать последнюю версию Android Studio.

Список элементов, которые должны быть правильно настроены перед началом работы в эспрессо-фреймворке, выглядит следующим образом:

  • Установите последнюю версию Java JDK и настройте переменную среды JAVA_HOME.

  • Установите последнюю версию Android Studio (версия 3.2. Или выше).

  • Установите последнюю версию Android SDK с помощью SDK Manager и настройте переменную среды ANDROID_HOME.

  • Установите последнюю версию Gradle Build Tool и настройте переменную среды GRADLE_HOME.

Установите последнюю версию Java JDK и настройте переменную среды JAVA_HOME.

Установите последнюю версию Android Studio (версия 3.2. Или выше).

Установите последнюю версию Android SDK с помощью SDK Manager и настройте переменную среды ANDROID_HOME.

Установите последнюю версию Gradle Build Tool и настройте переменную среды GRADLE_HOME.

Настроить EspressoTesting Framework

Изначально среда тестирования эспрессо предоставляется как часть библиотеки поддержки Android. Позже, команда Android предоставляет новую библиотеку Android, AndroidX и переносит в библиотеку новейшую разработку фреймворка для эспрессо. Последние разработки (Android 9.0, API уровня 28 или выше) фреймворка для эспрессо будут выполнены в библиотеке AndroidX.

Включить инфраструктуру тестирования эспрессо в проект так же просто, как установить среду тестирования эспрессо, как зависимость в файле gradle приложения app / build.gradle. Полная конфигурация следующая,

Используя библиотеку поддержки Android,

android {
   defaultConfig {
      testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
   }
}
dependencies {
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'com.android.support.test:runner:1.0.2'
   androidTestImplementation 'com.android.support.test.espresso:espressocore:3.0.2'
}

Используя библиотеку AndroidX,

android {
   defaultConfig {
      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }
}
dependencies {
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'com.androidx.test:runner:1.0.2'
   androidTestImplementation 'com.androidx.espresso:espresso-core:3.0.2'
}

testInstrumentationRunner в android / defaultConfig устанавливает класс AndroidJUnitRunner для запуска инструментальных тестов. Первая строка в зависимостях включает инфраструктуру тестирования JUnit , вторая строка в зависимостях включает библиотеку бегуна тестов для запуска тестовых случаев, и, наконец, третья строка в зависимостях включает инфраструктуру тестирования эспрессо.

По умолчанию Android Studio устанавливает структуру тестирования эспрессо (библиотека поддержки Android) как зависимость при создании проекта Android, и Gradle загружает необходимую библиотеку из репозитория Maven. Давайте создадим простое приложение для Android Hello World и проверим, правильно ли настроена среда тестирования эспрессо.

Шаги для создания нового приложения Android описаны ниже —

  • Запустите Android Studio.

  • Выберите Файл → Новый → Новый проект.

  • Введите имя приложения (HelloWorldApp) и домен компании (espressosamples.tutorialspoint.com) и нажмите кнопку Далее .

Запустите Android Studio.

Выберите Файл → Новый → Новый проект.

Введите имя приложения (HelloWorldApp) и домен компании (espressosamples.tutorialspoint.com) и нажмите кнопку Далее .

Приложение для Android

Чтобы создать Android-проект,

  • Выберите минимальный API в качестве API 15: Android 4.0.3 (IceCreamSandwich) и нажмите кнопку Далее.

Выберите минимальный API в качестве API 15: Android 4.0.3 (IceCreamSandwich) и нажмите кнопку Далее.

Целевые Android-устройства

Чтобы настроить устройства Android,

  • Выберите « Пустое действие» и нажмите « Далее» .

Выберите « Пустое действие» и нажмите « Далее» .

Пустая активность

Чтобы добавить активность в Mobile,

  • Введите имя для основной деятельности и затем нажмите Готово .

Введите имя для основной деятельности и затем нажмите Готово .

Основная деятельность

Чтобы настроить активность,

  • После создания нового проекта откройте файл app / build.gradle и проверьте его содержимое. Содержание файла указано ниже,

После создания нового проекта откройте файл app / build.gradle и проверьте его содержимое. Содержание файла указано ниже,

apply plugin: 'com.android.application'
android {
   compileSdkVersion 28
   defaultConfig {
      applicationId "com.tutorialspoint.espressosamples.helloworldapp"
      minSdkVersion 15
      targetSdkVersion 28
      versionCode 1
      versionName "1.0"
      testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
   }
   buildTypes {
      release {
         minifyEnabled false
         proguardFiles getDefaultProguardFile('proguard-android.txt'),    'proguard-rules.pro'
      }
   }
}
dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation 'com.android.support:appcompat-v7:28.0.0'
   implementation 'com.android.support.constraint:constraint-layout:1.1.3'
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'com.android.support.test:runner:1.0.2'
   androidTestImplementation 'com.android.support.test.espresso:espressocore:3.0.2'
}

Последняя строка указывает зависимость фреймворка эспрессо. По умолчанию библиотека поддержки Android настроена. Мы можем перенастроить приложение для использования библиотеки AndroidX , щелкнув RefactorMigrate to AndroidX в меню.

Эспрессо-тестирование

Чтобы перейти на Androidx,

  • Теперь app / build.gradle изменяется, как указано ниже,

Теперь app / build.gradle изменяется, как указано ниже,

apply plugin: 'com.android.application'
android {
   compileSdkVersion 28
   defaultConfig {
      applicationId "com.tutorialspoint.espressosamples.helloworldapp"
      minSdkVersion 15
      targetSdkVersion 28
      versionCode 1
      versionName "1.0"
      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }
   buildTypes {
      release {
         minifyEnabled false
         proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
      }
   }
}
dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
   implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'androidx.test:runner:1.1.1'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

Теперь последняя строка включает среду тестирования эспрессо из библиотеки AndroidX.

Настройки устройства

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

Давайте посмотрим, как отключить анимацию на устройствах Android — (Настройки → Параметры разработчика),

  • Масштаб оконной анимации

  • Масштаб перехода анимации

  • Шкала продолжительности аниматора

Масштаб оконной анимации

Масштаб перехода анимации

Шкала продолжительности аниматора

Если меню параметров разработчика недоступно на экране « Настройки» , нажмите « Номер сборки», доступный внутри параметра « О телефоне», несколько раз. Это включает меню параметров разработчика .

Запуск тестов в Android Studio

В этой главе давайте посмотрим, как запускать тесты с помощью Android studio.

Каждое приложение для Android имеет два типа тестов —

  • Функциональные / модульные тесты

  • Контрольно-измерительные приборы

Функциональные / модульные тесты

Контрольно-измерительные приборы

Функциональный тест не требует установки и запуска фактического приложения для Android в устройстве или эмуляторе и тестирования функциональности. Его можно запустить в самой консоли, не вызывая фактическое приложение. Однако инструментальные тесты требуют запуска самого приложения для проверки функциональности, такой как пользовательский интерфейс и взаимодействие с пользователем. По умолчанию модульные тесты записываются в папку src / test / java /, а тесты инструментовки — в папку src / androidTest / java / . Android-студия предоставляет контекстное меню « Выполнить» для тестовых классов, чтобы запустить тест, написанный в выбранных тестовых классах. По умолчанию приложение Android имеет два класса — ExampleUnitTest в папке src / test и ExampleInstrumentedTest в папке src / androidTest .

Чтобы запустить модульный тест по умолчанию, выберите ExampleUnitTest в студии Android, щелкните правой кнопкой мыши по нему и затем нажмите « Выполнить ExampleUnitTest», как показано ниже,

Android Studio

Выполнить юнит-тест

Это запустит модульное тестирование и покажет результат в консоли, как на следующем скриншоте —

Проверить и показать

Успешное тестирование

Чтобы запустить контрольно-измерительный прибор по умолчанию, выберите ExampleInstrumentationTest в Android Studio, щелкните правой кнопкой мыши по нему и затем нажмите Run «ExampleInstrumentationTest», как показано ниже,

Тестирование инструментовки

Выполнить тестирование приборов

Это запустит модульное тестирование, запустив приложение на устройстве или в эмуляторе, и покажет результат в консоли, как на следующем снимке экрана:

Модульный тест

Тестирование прибора прошло успешно.

Espresso Testing Framework — Обзор JUnit

В этой главе давайте разберемся с основами JUnit , популярной среды модульного тестирования, разработанной сообществом Java, на которой построена среда тестирования эспрессо.

JUnit является стандартом де-факто для модульного тестирования Java-приложений. Несмотря на то, что он популярен для модульного тестирования, он также имеет полную поддержку и возможности для тестирования приборов. Библиотека тестирования Espresso расширяет необходимые классы JUnit для поддержки инструментального тестирования на базе Android.

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

Давайте создадим Java-класс Computation (Computation.java) и напишем простую математическую операцию суммирования и умножения . Затем мы напишем контрольные примеры, используя JUnit, и проверим его, выполнив контрольные примеры.

  • Запустите Android Studio.

  • Откройте HelloWorldApp, созданный в предыдущей главе.

  • Создайте файл Computation.java в приложении / src / main / java / com / tutorialspoint / espressosamples / helloworldapp / и напишите две функции — Sum и Multiply, как указано ниже,

Запустите Android Studio.

Откройте HelloWorldApp, созданный в предыдущей главе.

Создайте файл Computation.java в приложении / src / main / java / com / tutorialspoint / espressosamples / helloworldapp / и напишите две функции — Sum и Multiply, как указано ниже,

package com.tutorialspoint.espressosamples.helloworldapp;
public class Computation {
   public Computation() {}
   public int Sum(int a, int b) {
      return a + b;
   }
   public int Multiply(int a, int b) {
      return a * b;
   }
}
  • Создайте файл ComputationUnitTest.java в app / src / test / java / com / tutorialspoint / espressosamples / helloworldapp и напишите тестовые примеры для проверки функциональности Sum и Multiply, как указано ниже.

Создайте файл ComputationUnitTest.java в app / src / test / java / com / tutorialspoint / espressosamples / helloworldapp и напишите тестовые примеры для проверки функциональности Sum и Multiply, как указано ниже.

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ComputationUnitTest {
   @Test
   public void sum_isCorrect() {
      Computation computation = new Computation();
      assertEquals(4, computation.Sum(2,2));
   }
   @Test
   public void multiply_isCorrect() {
      Computation computation = new Computation();
      assertEquals(4, computation.Multiply(2,2));
   }
}

Здесь мы использовали два новых термина — @Test и assertEquals . В общем, JUnit использует аннотацию Java для идентификации тестовых случаев в классе и информации о том, как выполнить тестовые случаи. @Test — одна из таких аннотаций Java, которая указывает, что конкретная функция является тестовым примером junit. assertEquals — это функция, которая утверждает, что первый аргумент (ожидаемое значение) и второй аргумент (вычисленное значение) равны и одинаковы. JUnit предоставляет несколько методов подтверждения для различных тестовых сценариев.

  • Теперь запустите ComputationUnitTest в Android-студии, щелкнув правой кнопкой мыши по классу и вызвав опцию Run ‘ComputationUnitTest’, как описано в предыдущей главе. Это запустит случаи модульного тестирования и сообщит об успехе.

Теперь запустите ComputationUnitTest в Android-студии, щелкнув правой кнопкой мыши по классу и вызвав опцию Run ‘ComputationUnitTest’, как описано в предыдущей главе. Это запустит случаи модульного тестирования и сообщит об успехе.

Результат вычислительного модульного теста показан ниже:

Вычислительный юнит тест

Аннотации

Инфраструктура JUnit широко использует аннотации . Вот некоторые важные аннотации:

  • @Тестовое задание

  • @До

  • @После

  • @BeforeClass

  • @После школы

  • @Rule

@Тестовое задание

@До

@После

@BeforeClass

@После школы

@Rule

@Test аннотация

@Test — это очень важная аннотация в среде JUnit . @Test используется для дифференциации обычного метода от метода контрольного примера. После того, как метод снабжен аннотацией @Test , этот конкретный метод считается тестовым примером и будет выполняться JUnit Runner . JUnit Runner — это специальный класс, который используется для поиска и запуска тестовых случаев JUnit, доступных внутри классов Java. На данный момент мы используем встроенную опцию Android Studio для запуска модульных тестов (которые, в свою очередь, запускают JUnit Runner ). Пример кода выглядит следующим образом:

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class ComputationUnitTest {
   @Test
   public void multiply_isCorrect() {
      Computation computation = new Computation();
      assertEquals(4, computation.Multiply(2,2));
   }
}

@До

@ Перед аннотацией используется для ссылки на метод, который необходимо вызвать перед запуском любого метода тестирования, доступного в определенном классе теста. Например, в нашем примере объект Computation может быть создан в отдельном методе и помечен @Before, чтобы он выполнялся как перед тестовым случаем sum_isCorrect, так и multiply_isCorrect . Полный код выглядит следующим образом:

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class ComputationUnitTest {
   Computation computation = null;
   @Before
   public void CreateComputationObject() {
      this.computation = new Computation();
   }
   @Test
   public void sum_isCorrect() {
      assertEquals(4, this.computation.Sum(2,2));
   }
   @Test
   public void multiply_isCorrect() {
      assertEquals(4, this.computation.Multiply(2,2));
   }
}

@После

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

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class ComputationUnitTest {
   Computation computation = null;
   @Before
   public void CreateComputationObject() {
      this.computation = new Computation();
   }
   @After
   public void DestroyComputationObject() {
      this.computation = null;
   }
   @Test
   public void sum_isCorrect() {
      assertEquals(4, this.computation.Sum(2,2));
   }
   @Test
   public void multiply_isCorrect() {
      assertEquals(4, this.computation.Multiply(2,2));
   }
}

@BeforeClass

@BeforeClass аналогичен @Before , но метод, аннотированный @BeforeClass, будет вызван или выполнен только один раз перед выполнением всех тестовых случаев в определенном классе. Полезно создать ресурсоемкий объект, такой как объект подключения к базе данных. Это сократит время выполнения набора тестов. Этот метод должен быть статическим для правильной работы. В нашем примере мы можем создать вычислительный объект один раз перед выполнением всех тестовых случаев, как указано ниже,

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class ComputationUnitTest {
   private static Computation computation = null;
   @BeforeClass
   public static void CreateComputationObject() {
      computation = new Computation();
   }
   @Test
   public void sum_isCorrect() {
      assertEquals(4, computation.Sum(2,2));
   }
   @Test
   public void multiply_isCorrect() {
      assertEquals(4, computation.Multiply(2,2));
   }
}

@После школы

@AfterClass похож на @BeforeClass , но метод, аннотированный @AfterClass, будет вызываться или выполняться только один раз после запуска всех тестовых случаев в определенном классе. Этот метод также должен быть статическим для правильной работы. Пример кода выглядит следующим образом —

package com.tutorialspoint.espressosamples.helloworldapp;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class ComputationUnitTest {
   private static Computation computation = null;
   @BeforeClass
   public static void CreateComputationObject() {
      computation = new Computation();
   }
   @AfterClass
   public static void DestroyComputationObject() {
      computation = null;
   }
   @Test
   public void sum_isCorrect() {
      assertEquals(4, computation.Sum(2,2));
   }
   @Test
   public void multiply_isCorrect() {
      assertEquals(4, computation.Multiply(2,2));
   }
}

@Rule

@Rule аннотация является одним из основных моментов JUnit . Он используется для добавления поведения в тестовые случаи. Мы можем только аннотировать поля типа TestRule . Он фактически предоставляет набор функций, предоставляемых аннотациями @Before и @After, но эффективным и многократно используемым способом. Например, нам может понадобиться временная папка для хранения некоторых данных во время теста. Обычно нам нужно создать временную папку перед запуском тестового примера (используя аннотацию @Before или @BeforeClass) и уничтожить ее после запуска тестового примера (используя аннотацию @After или @AfterClass). Вместо этого мы можем использовать класс TemporaryFolder (типа TestRule ), предоставляемый платформой JUnit, чтобы создать временную папку для всех наших тестовых случаев, и временная папка будет удалена по мере запуска тестового примера. Нам нужно создать новую переменную типа TemporaryFolder и добавить аннотацию @Rule, как указано ниже,

package com.tutorialspoint.espressosamples.helloworldapp;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;

public class ComputationUnitTest {
   private static Computation computation = null;
   @Rule
   public TemporaryFolder folder = new TemporaryFolder();
   @Test
   public void file_isCreated() throws IOException {
      folder.newFolder("MyTestFolder");
      File testFile = folder.newFile("MyTestFile.txt");
      assertTrue(testFile.exists());
   }
   @BeforeClass
   public static void CreateComputationObject() {
      computation = new Computation();
   }
   @AfterClass
   public static void DestroyComputationObject() {
      computation = null;
   }
   @Test
   public void sum_isCorrect() {
      assertEquals(4, computation.Sum(2,2));
   }
   @Test
   public void multiply_isCorrect() {
      assertEquals(4, computation.Multiply(2,2));
   }
}

Порядок исполнения

В JUnit методы, помеченные различными аннотациями, будут выполняться в определенном порядке, как показано ниже,

  • @BeforeClass

  • @Rule

  • @До

  • @Тестовое задание

  • @После

  • @После школы

@BeforeClass

@Rule

@До

@Тестовое задание

@После

@После школы

Утверждение

Утверждение — это способ проверки, соответствует ли ожидаемое значение контрольного примера фактическому значению результата контрольного примера. JUnit обеспечивает утверждение для другого сценария; несколько важных утверждений перечислены ниже —

  • fail () — явно сделать тестовый случай неудачным.

  • assertTrue (boolean test_condition) — проверяет, что test_condition является истинным

  • assertFalse (логическое test_condition) — проверяет, что test_condition имеет значение false

  • assertEquals (ожидаемый, фактический) — проверяет, что оба значения равны

  • assertNull (object) — проверяет, что объект является нулевым

  • assertNotNull (object) — проверяет, что объект не является нулевым

  • assertSame (ожидаемый, фактический) — проверяет, что оба ссылаются на один и тот же объект.

  • assertNotSame (ожидаемый, фактический) — проверяет, что оба ссылаются на разные объекты.

fail () — явно сделать тестовый случай неудачным.

assertTrue (boolean test_condition) — проверяет, что test_condition является истинным

assertFalse (логическое test_condition) — проверяет, что test_condition имеет значение false

assertEquals (ожидаемый, фактический) — проверяет, что оба значения равны

assertNull (object) — проверяет, что объект является нулевым

assertNotNull (object) — проверяет, что объект не является нулевым

assertSame (ожидаемый, фактический) — проверяет, что оба ссылаются на один и тот же объект.

assertNotSame (ожидаемый, фактический) — проверяет, что оба ссылаются на разные объекты.

Espresso Testing Framework — Архитектура

В этой главе мы узнаем об условиях фреймворка эспрессо, о том, как написать простой тестовый эспрессо, и о полном рабочем процессе или архитектуре фреймворка эспрессо.

обзор

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

Юнит бегун

Платформа тестирования Android предоставляет средство запуска AndroidJUnitRunner для запуска тестовых случаев эспрессо, написанных в тестовых примерах стиля JUnit3 и JUnit4. Он специфичен для приложений Android и прозрачно обрабатывает загрузку тестовых наборов для эспрессо и тестируемого приложения как в реальном устройстве, так и в эмуляторе, выполняет тестовые наборы и сообщает о результатах тестовых примеров. Чтобы использовать AndroidJUnitRunner в тестовом примере, нам нужно аннотировать тестовый класс с помощью аннотации @RunWith, а затем передать аргумент AndroidJUnitRunner, как указано ниже:

@RunWith(AndroidJUnit4.class)
   public class ExampleInstrumentedTest {
}

Юнит правила

Платформа тестирования Android предоставляет правило ActivityTestRule для запуска активности Android перед выполнением тестовых случаев. Он запускает действие перед каждым методом, аннотированным @ Test` и @Before. Он прервет действие после метода, помеченного @After. Пример кода выглядит следующим образом:

@Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);

Здесь MainActivity — это действие, которое должно быть запущено перед запуском тестового примера и уничтожено после запуска конкретного тестового примера.

ViewMatchers

Espresso предоставляет большое количество классов сопоставления представлений (в пакете androidx.test.espresso.matcher.ViewMatchers ) для сопоставления и поиска элементов / представлений пользовательского интерфейса в иерархии представлений экрана активности Android. Метод Espresso onView принимает единственный аргумент типа Matcher (View matchers), находит соответствующее представление пользовательского интерфейса и возвращает соответствующий объект ViewInteraction . Объект ViewInteraction, возвращаемый методом onView, может в дальнейшем использоваться для вызова таких действий, как щелчок по сопоставленному представлению, или может использоваться для утверждения сопоставленного представления. Пример кода для поиска представления с текстом «Hello World!» Выглядит следующим образом:

ViewInteraction viewInteraction = Espresso.onView(withText("Hello World!"));

Здесь withText — это средство сопоставления, которое можно использовать для сопоставления представления пользовательского интерфейса, имеющего текст «Hello World!»

ViewActions

Espresso предоставляет большое количество классов действий представления (в androidx.test.espresso.action.ViewActions), чтобы вызывать различные действия в выбранном / согласованном представлении. Как только onView сопоставляет и возвращает объект ViewInteraction , любое действие может быть вызвано путем вызова метода « execute» объекта ViewInteraction и передачи его с соответствующими действиями представления. Пример кода для выбора подходящего вида выглядит следующим образом:

ViewInteraction viewInteraction = Espresso.onView(withText("Hello World!"));
viewInteraction.perform(click());

Здесь будет выполнено действие щелчка соответствующего вида.

ViewAssertions

Аналогично представлениям соответствия и действиям представления, Espresso предоставляет большое количество утверждений представления (в пакете androidx.test.espresso.assertion.ViewAssertions ), чтобы утверждать, что согласованное представление — то, что мы ожидали. Как только onView совпадает и возвращает объект ViewInteraction , любое утверждение может быть проверено с использованием метода проверки ViewInteraction , передав его с правильным утверждением представления. Пример кода, чтобы утверждать, что согласованное представление выглядит следующим образом:

ViewInteraction viewInteraction = Espresso.onView(withText("Hello World!"));
viewInteraction.check(matches(withId(R.id.text_view)));

Здесь совпадения принимают сопоставление вида и возвращают утверждение представления, что можно проверить методом проверки ViewInteraction .

Рабочий процесс Espresso Testing Framework

Давайте разберемся, как работает фреймворк для эспрессо и как он предоставляет возможности для любого пользовательского взаимодействия простым и гибким способом. Рабочий процесс теста эспрессо, как описано ниже,

  • Как мы узнали ранее, Android JUnit Runner, AndroidJUnit4 будет запускать тестовые случаи Android. Тестовые наборы для эспрессо должны быть помечены @RunWith (AndroidJUnut.class) . Во-первых, AndroidJUnit4 подготовит среду для запуска тестовых случаев. Он запускает подключенное устройство Android или эмулятор, устанавливает приложение и проверяет, находится ли тестируемое приложение в состоянии готовности. Он запустит тестовые случаи и сообщит о результатах.

  • Эспрессо требуется по крайней мере одно правило JUnit типа ActivityTestRule, чтобы указать действие. Android JUnit runner запустит действие, которое будет запущено с помощью ActivityTestRule .

  • В каждом тестовом примере требуется как минимум один вызов метода onView или onDate (используется для поиска представлений на основе данных, таких как AdapterView ) для сопоставления и поиска нужного представления. onView или onData возвращает объект ViewInteraction .

  • Как только объект ViewInteraction возвращается, мы можем либо вызвать действие выбранного представления, либо проверить представление на ожидаемое представление, используя утверждение.

  • Действие можно вызвать с помощью метода execute объекта ViewInteraction , передав любое из доступных действий просмотра.

  • Утверждение может быть вызвано с помощью метода check объекта ViewInteraction , передав любое из доступных утверждений представления.

Как мы узнали ранее, Android JUnit Runner, AndroidJUnit4 будет запускать тестовые случаи Android. Тестовые наборы для эспрессо должны быть помечены @RunWith (AndroidJUnut.class) . Во-первых, AndroidJUnit4 подготовит среду для запуска тестовых случаев. Он запускает подключенное устройство Android или эмулятор, устанавливает приложение и проверяет, находится ли тестируемое приложение в состоянии готовности. Он запустит тестовые случаи и сообщит о результатах.

Эспрессо требуется по крайней мере одно правило JUnit типа ActivityTestRule, чтобы указать действие. Android JUnit runner запустит действие, которое будет запущено с помощью ActivityTestRule .

В каждом тестовом примере требуется как минимум один вызов метода onView или onDate (используется для поиска представлений на основе данных, таких как AdapterView ) для сопоставления и поиска нужного представления. onView или onData возвращает объект ViewInteraction .

Как только объект ViewInteraction возвращается, мы можем либо вызвать действие выбранного представления, либо проверить представление на ожидаемое представление, используя утверждение.

Действие можно вызвать с помощью метода execute объекта ViewInteraction , передав любое из доступных действий просмотра.

Утверждение может быть вызвано с помощью метода check объекта ViewInteraction , передав любое из доступных утверждений представления.

Диаграмма представления рабочего процесса выглядит следующим образом:

WorkFlow

Пример — просмотр утверждения

Давайте напишем простой тестовый пример, чтобы найти текстовое представление с текстом «Hello World!» В нашем приложении «HelloWorldApp», а затем подтвердить его с помощью утверждения представления. Полный код выглядит следующим образом:

package com.tutorialspoint.espressosamples.helloworldapp;

import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.withText;;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static org.junit.Assert.*;
/**
   * Instrumented test, which will execute on an Android device.
   *
   * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
   @Rule
   public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
   @Test
   public void view_isCorrect() {
      onView(withText("Hello World!")).check(matches(isDisplayed()));
   }
   @Test
   public void useAppContext() {
      // Context of the app under test.
      Context appContext = InstrumentationRegistry.getTargetContext();
      assertEquals("com.tutorialspoint.espressosamples.helloworldapp", appContext.getPackageName());
   }
}

Здесь мы использовали сопоставители с текстовым представлением, чтобы найти текстовое представление, содержащее текст «Hello World!» И соответствующее утверждение представления, чтобы утверждать, что текстовое представление отображается правильно. После запуска тестового примера в Android Studio он запустит тестовый отчет и сообщит об успешном завершении, как показано ниже.

view_isПравильный тестовый пример

Прецедент

Espresso Testing Framework — Просмотр совпадений

Фреймворк эспрессо предоставляет множество видов соответствия. Целью сопоставления является сопоставление представления с использованием различных атрибутов представления, таких как Id, Text и доступность дочернего представления. Каждый сопоставитель соответствует определенным атрибутам представления и применяется к определенному типу представления. Например, сопоставление withId сопоставляет свойство Id представления и применяется ко всему представлению, тогда как сопоставление withText сопоставляет свойство Text представления и применяется только к TextView .

В этой главе мы изучим различные средства сопоставления, предоставляемые средой тестирования эспрессо, а также изучим библиотеку Hamcrest, на которой построены средства сопоставления эспрессо.

Библиотека Хамкрест

Библиотека Hamcrest является важной библиотекой в ​​сфере тестирования эспрессо. Hamcrest сам по себе является основой для написания объектов соответствия. Фреймворк Espresso широко использует библиотеку Hamcrest и расширяет ее по мере необходимости, обеспечивая простые и расширяемые средства сравнения.

Hamcrest предоставляет простую функцию assertThat и набор соответствий для утверждения любых объектов. assertThat имеет три аргумента, и они как показано ниже —

  • Строка (описание теста, необязательно)

  • Объект (актуальный)

  • Matcher (ожидается)

Строка (описание теста, необязательно)

Объект (актуальный)

Matcher (ожидается)

Давайте напишем простой пример, чтобы проверить, имеет ли объект списка ожидаемое значение.

import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
@Test
public void list_hasValue() {
   ArrayList<String> list = new ArrayList<String>();
   list.add("John");
   assertThat("Is list has John?", list, hasItem("John"));
}

Здесь hasItem возвращает совпадение , которое проверяет, имеет ли фактический список указанное значение в качестве одного из элементов.

В Hamcrest имеется множество встроенных сопоставителей, а также есть опции для создания новых сопоставителей. Вот некоторые из важных встроенных средств сравнения, полезных в среде тестирования эспрессо:

что угодно — всегда соответствует

Логические совпадения

  • allOf — принимать любое количество совпадений и совпадений только в том случае, если все совпадения пройдены успешно.

  • anyOf — принять любое количество совпадений и совпадений, если удастся выполнить одно совпадение.

  • not — принять одно совпадение и совпадения только в случае сбоя сопоставления и наоборот.

allOf — принимать любое количество совпадений и совпадений только в том случае, если все совпадения пройдены успешно.

anyOf — принять любое количество совпадений и совпадений, если удастся выполнить одно совпадение.

not — принять одно совпадение и совпадения только в случае сбоя сопоставления и наоборот.

Текстовые совпадения

  • equalToIgnoringCase — используется для проверки, равен ли фактический ввод ожидаемому регистру игнорирования строки.

  • equalToIgnoringWhiteSpace — используется для проверки, равен ли фактический ввод заданной строке, игнорируя регистр и пробелы.

  • containsString — используется для проверки того, содержит ли фактический ввод указанную строку.

  • endWith — используется для проверки, начинается ли фактический ввод с указанной строки.

  • startWith — используется для проверки того, заканчивается ли ввод указанной строкой.

equalToIgnoringCase — используется для проверки, равен ли фактический ввод ожидаемому регистру игнорирования строки.

equalToIgnoringWhiteSpace — используется для проверки, равен ли фактический ввод заданной строке, игнорируя регистр и пробелы.

containsString — используется для проверки того, содержит ли фактический ввод указанную строку.

endWith — используется для проверки, начинается ли фактический ввод с указанной строки.

startWith — используется для проверки того, заканчивается ли ввод указанной строкой.

Числовые совпадения

  • closeTo — используется для проверки, близок ли фактический ввод к ожидаемому числу.

  • moreThan — используется для проверки того, больше ли фактический ввод, чем ожидаемое число.

  • moreThanOrEqualTo — используется для проверки того, является ли фактический ввод больше или равен ожидаемому числу.

  • lessThan — используется для проверки того, меньше ли фактический ввод ожидаемого числа.

  • lessThanOrEqualTo — используется для проверки того, является ли фактический вход меньше или равен ожидаемому числу.

closeTo — используется для проверки, близок ли фактический ввод к ожидаемому числу.

moreThan — используется для проверки того, больше ли фактический ввод, чем ожидаемое число.

moreThanOrEqualTo — используется для проверки того, является ли фактический ввод больше или равен ожидаемому числу.

lessThan — используется для проверки того, меньше ли фактический ввод ожидаемого числа.

lessThanOrEqualTo — используется для проверки того, является ли фактический вход меньше или равен ожидаемому числу.

Сопоставление объектов на основе

  • equalTo — используется для проверки, равен ли фактический ввод ожидаемому объекту

  • hasToString — используется для проверки, имеет ли фактический ввод метод toString.

  • instanceOf — используется для проверки того, является ли фактический ввод экземпляром ожидаемого класса.

  • isCompatibleType — используется для проверки совместимости фактического ввода с ожидаемым типом.

  • notNullValue — используется для проверки, не является ли фактический ввод нулевым.

  • sameInstance — используется для проверки того, являются ли фактические входные и ожидаемые значения одинаковыми.

  • hasProperty — используется для проверки того, имеет ли фактический ввод ожидаемое свойство

equalTo — используется для проверки, равен ли фактический ввод ожидаемому объекту

hasToString — используется для проверки, имеет ли фактический ввод метод toString.

instanceOf — используется для проверки того, является ли фактический ввод экземпляром ожидаемого класса.

isCompatibleType — используется для проверки совместимости фактического ввода с ожидаемым типом.

notNullValue — используется для проверки, не является ли фактический ввод нулевым.

sameInstance — используется для проверки того, являются ли фактические входные и ожидаемые значения одинаковыми.

hasProperty — используется для проверки того, имеет ли фактический ввод ожидаемое свойство

это — сахар или короткий путь для равных

Matchers

Espresso предоставляет метод onView () для сопоставления и поиска представлений. Он принимает сопоставления представлений и возвращает объект ViewInteraction для взаимодействия с сопоставленным представлением. Часто используемый список сопоставлений представлений описан ниже —

withId ()

withId () принимает аргумент типа int, а аргумент ссылается на идентификатор представления. Возвращает совпадение, которое соответствует представлению, используя идентификатор представления. Пример кода выглядит следующим образом:

onView(withId(R.id.testView))

withText ()

withText () принимает аргумент типа string, а аргумент ссылается на значение свойства text представления. Он возвращает совпадение, которое соответствует представлению, используя текстовое значение представления. Это относится только к TextView . Пример кода выглядит следующим образом:

onView(withText("Hello World!"))

withContentDescription ()

withContentDescription () принимает аргумент типа string, а аргумент ссылается на значение свойства описания содержимого представления. Возвращает совпадение, которое соответствует представлению, используя описание представления. Пример кода выглядит следующим образом:

onView(withContentDescription("blah"))

Мы также можем передать идентификатор ресурса текстового значения вместо самого текста.

onView(withContentDescription(R.id.res_id_blah))

hasContentDescription ()

hasContentDescription () не имеет аргумента. Он возвращает совпадение, которое соответствует представлению, имеющему любое описание содержимого. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), hasContentDescription()))

withTagKey ()

withTagKey () принимает аргумент типа string, а аргумент ссылается на ключ тега представления. Он возвращает совпадение, которое соответствует представлению, используя его ключ тега. Пример кода выглядит следующим образом:

onView(withTagKey("blah"))

Мы также можем передать идентификатор ресурса имени тега вместо самого имени тега.

onView(withTagKey(R.id.res_id_blah))

withTagValue ()

withTagValue () принимает аргумент типа Matcher <Object>, и этот аргумент ссылается на значение тега представления. Он возвращает совпадение, которое соответствует представлению, используя его значение тега. Пример кода выглядит следующим образом:

onView(withTagValue(is((Object) "blah")))

Вот Хэмкрест Матчер.

withClassName ()

withClassName () принимает аргумент типа Matcher <String>, и этот аргумент ссылается на значение имени класса представления. Он возвращает совпадение, которое соответствует представлению, используя его имя класса. Пример кода выглядит следующим образом:

onView(withClassName(endsWith("EditText")))

Здесь endWith совпадает с Hamcrest и возвращает Matcher <String>

withHint ()

withHint () принимает аргумент типа Matcher <String>, и этот аргумент ссылается на значение подсказки представления. Возвращает совпадение, которое соответствует представлению, используя подсказку представления. Пример кода выглядит следующим образом:

onView(withClassName(endsWith("Enter name")))

withInputType ()

withInputType () принимает аргумент типа int, а аргумент ссылается на тип ввода представления. Он возвращает совпадение, которое соответствует представлению, используя его тип ввода. Пример кода выглядит следующим образом:

onView(withInputType(TYPE_CLASS_DATETIME))

Здесь TYPE_CLASS_DATETIME ссылается на представление редактирования, поддерживающее дату и время.

withResourceName ()

withResourceName () принимает аргумент типа Matcher <String>, и этот аргумент ссылается на значение имени класса представления. Он возвращает совпадение, которое соответствует представлению, используя имя ресурса представления. Пример кода выглядит следующим образом:

onView(withResourceName(endsWith("res_name")))

Он также принимает строковый аргумент. Пример кода выглядит следующим образом:

onView(withResourceName("my_res_name"))

withAlpha ()

withAlpha () принимает аргумент типа float, и этот аргумент ссылается на альфа-значение представления. Он возвращает совпадение, которое соответствует представлению, используя альфа-значение представления. Пример кода выглядит следующим образом:

onView(withAlpha(0.8))

withEffectiveVisibility ()

withEffectiveVisibility () принимает аргумент типа ViewMatchers.Visibility, и этот аргумент ссылается на эффективную видимость представления. Возвращает сопоставление, которое соответствует представлению, используя видимость представления. Пример кода выглядит следующим образом:

onView(withEffectiveVisibility(withEffectiveVisibility.INVISIBLE))

withSpinnerText ()

withSpinnerText () принимает аргумент типа Matcher <String>, и этот аргумент ссылается на текущее значение выбранного представления Spinner. Он возвращает совпадение, которое соответствует счетчику на основе значения toString выбранного элемента. Пример кода выглядит следующим образом:

onView(withSpinnerText(endsWith("USA")))

Он также принимает строковый аргумент или идентификатор ресурса строки. Пример кода выглядит следующим образом:

onView(withResourceName("USA"))
onView(withResourceName(R.string.res_usa))

withSubstring ()

Метод withSubString () похож на метод withText (), за исключением того, что он помогает проверить подстроку текстового значения представления.

onView(withSubString("Hello"))

hasLinks ()

hasLinks () не имеет аргументов и возвращает совпадение, которое соответствует представлению, имеющему ссылки. Это относится только к TextView. Пример кода выглядит следующим образом:

onView(allOf(withSubString("Hello"), hasLinks()))

Здесь allOf — это матч Хэмкрест. allOf возвращает сопоставление, которое соответствует всем переданным в сопоставителях, и здесь оно используется для сопоставления представления, а также для проверки наличия в представлении ссылок в его текстовом значении.

hasTextColor ()

hasTextColor () принимает один аргумент типа int, и этот аргумент ссылается на идентификатор ресурса цвета. Он возвращает совпадение, которое соответствует TextView в зависимости от его цвета. Это относится только к TextView . Пример кода выглядит следующим образом:

onView(allOf(withSubString("Hello"), hasTextColor(R.color.Red)))

hasEllipsizedText ()

hasEllipsizedText () не имеет аргумента. Он возвращает совпадение, которое соответствует TextView с длинным текстом, имеющим эллиптический размер (первый … десять … последний) или обрезанный (первый …). Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_text_view_id), hasEllipsizedText()))

hasMultilineText ()

hasMultilineText () не имеет аргумента. Он возвращает совпадение, которое совпадает с TextView, имеющим любой многострочный текст. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_test_view_id), hasMultilineText()))

hasBackground ()

hasBackground () принимает один аргумент типа int, и этот аргумент ссылается на идентификатор ресурса фонового ресурса. Он возвращает сопоставление, которое соответствует представлению на основе его фоновых ресурсов. Пример кода выглядит следующим образом:

onView(allOf(withId("image"), hasBackground(R.drawable.your_drawable)))

hasErrorText ()

hasErrorText () принимает аргумент типа Matcher <String>, и этот аргумент ссылается на строковое значение ошибки представления (EditText). Он возвращает совпадение, которое соответствует представлению, используя строку ошибки представления. Это относится только к EditText . Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.editText_name), hasErrorText(is("name is required"))))

Он также принимает строковый аргумент. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.editText_name), hasErrorText("name is required")))

hasImeAction ()

hasImeAction () принимает аргумент типа Matcher <Integer>, и этот аргумент ссылается на поддерживаемые методы ввода представления (EditText). Он возвращает совпадение, которое соответствует представлению, используя поддерживаемый метод ввода представления. Это относится только к EditText . Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.editText_name),
hasImeAction(is(EditorInfo.IME_ACTION_GO))))

Здесь EditorInfo.IME_ACTION_GO является одним из параметров методов ввода. hasImeAction () также принимает целочисленный аргумент. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.editText_name),
hasImeAction(EditorInfo.IME_ACTION_GO)))

supportsInputMethods ()

supportInputMethods () не имеет аргументов. Он возвращает совпадение, которое соответствует представлению, если оно поддерживает методы ввода. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.editText_name), supportsInputMethods()))

isRoot ()

isRoot () не имеет аргументов. Возвращает совпадение, которое соответствует корневому представлению. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_root_id), isRoot()))

isDisplayed ()

isDisplayed () не имеет аргументов. Возвращает сопоставление, которое соответствует представлению, отображаемому в данный момент. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isDisplayed()))

isDisplayingAtLeast ()

isDisplayingAtLeast () принимает один аргумент типа int. Он возвращает сопоставление, которое соответствует текущему отображению, отображаемому как минимум с указанным процентом. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isDisplayingAtLeast(75)))

isCompletelyDisplayed ()

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

onView(allOf(withId(R.id.my_view_id), isCompletelyDisplayed()))

включен()

isEnabled () не имеет аргумента. Он возвращает совпадение, которое соответствует представлению, которое включено. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isEnabled()))

isFocusable ()

isFocusable () не имеет аргумента. Он возвращает совпадение, которое соответствует представлению, которое имеет опцию фокусировки. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isFocusable()))

HasFocus ()

hasFocus () не имеет аргументов. Он возвращает совпадение, которое соответствует представлению, которое в данный момент сфокусировано. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), hasFocus()))

isClickable ()

isClickable () не имеет аргументов. Он возвращает совпадение, которое соответствует представлению, которое является опцией щелчка. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isClickable()))

IsSelected ()

isSelected () не имеет аргумента. Возвращает сопоставление, которое соответствует представлению, выбранному в данный момент. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isSelected()))

проверено()

isChecked () не имеет аргументов. Он возвращает совпадение, которое соответствует представлению, имеющему тип CompoundButton (или его подтип) и находящемуся в проверенном состоянии. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isChecked()))

isNotChecked ()

isNotChecked () прямо противоположен isChecked. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_view_id), isNotChecked()))

isJavascriptEnabled ()

isJavascriptEnabled () не имеет аргументов. Он возвращает сопоставитель, который соответствует WebView, который оценивает JavaScript. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.my_webview_id), isJavascriptEnabled()))

withParent ()

withParent () принимает один аргумент типа Matcher <View>. Аргумент относится к мнению. Он возвращает совпадение, которое соответствует представлению, что указанное представление является родительским представлением. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.childView), withParent(withId(R.id.parentView))))

hasSibling ()

hasSibling () принимает один аргумент типа Matcher> View <. Аргумент относится к мнению. Он возвращает сопоставление, которое соответствует представлению, которое переданное представление является одним из его родственного представления. Пример кода выглядит следующим образом:

onView(hasSibling(withId(R.id.siblingView)))

с ребенком()

withChild () принимает один аргумент типа Matcher <View>. Аргумент относится к мнению. Он возвращает сопоставление, которое соответствует представлению, что переданное представление является дочерним представлением. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.parentView), withChild(withId(R.id.childView))))

hasChildCount ()

hasChildCount () принимает один аргумент типа int. Аргумент ссылается на количество дочерних элементов представления. Он возвращает совпадение, которое соответствует представлению, которое имеет точно такое же количество дочерних представлений, как указано в аргументе. Пример кода выглядит следующим образом:

onView(hasChildCount(4))

hasMinimumChildCount ()

hasMinimumChildCount () принимает один аргумент типа int. Аргумент ссылается на количество дочерних элементов представления. Он возвращает совпадение, которое соответствует представлению, имеющему, по крайней мере, номер дочернего представления, указанного в аргументе. Пример кода выглядит следующим образом:

onView(hasMinimumChildCount(4))

hasDescendant ()

hasDescendant () принимает один аргумент типа Matcher <View>. Аргумент относится к мнению. Он возвращает сопоставление, которое соответствует представлению, в котором переданное представление является одним из представлений-потомков в иерархии представлений. Пример кода выглядит следующим образом:

onView(hasDescendant(withId(R.id.descendantView)))

isDescendantOfA ()

isDescendantOfA () принимает один аргумент типа Matcher <View>. Аргумент относится к мнению. Он возвращает сопоставление, которое соответствует представлению, что переданное представление является одним из представлений предка в иерархии представлений. Пример кода выглядит следующим образом:

onView(allOf(withId(R.id.myView), isDescendantOfA(withId(R.id.parentView))))

Индивидуальные представления соответствия

Espresso предоставляет различные варианты для создания наших собственных сопоставителей представлений, и он основан на сопоставителях Hamcrest. Custom matcher — очень мощная концепция для расширения фреймворка, а также для настройки фреймворка на наш вкус. Некоторые из преимуществ написания пользовательских соответствий следующие:

  • Чтобы использовать уникальную особенность наших собственных пользовательских представлений

  • Настраиваемое сопоставление помогает в тестовых примерах на основе AdapterView соответствовать разным типам базовых данных.

  • Чтобы упростить текущие сопоставления путем объединения функций нескольких сопоставителей

Чтобы использовать уникальную особенность наших собственных пользовательских представлений

Настраиваемое сопоставление помогает в тестовых примерах на основе AdapterView соответствовать разным типам базовых данных.

Чтобы упростить текущие сопоставления путем объединения функций нескольких сопоставителей

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

Эспрессо предоставляет следующие два класса для написания новых соответствий —

  • TypeSafeMatcher

  • BoundedMatcher

TypeSafeMatcher

BoundedMatcher

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

  • Добавьте приведенную ниже зависимость в файл app / build.gradle и синхронизируйте ее.

Добавьте приведенную ниже зависимость в файл app / build.gradle и синхронизируйте ее.

dependencies {
   implementation 'androidx.test.espresso:espresso-core:3.1.1'
}
  • Создайте новый класс, чтобы включить наши соответствия (методы) и пометить его как окончательный

Создайте новый класс, чтобы включить наши соответствия (методы) и пометить его как окончательный

public final class MyMatchers {
}
  • Объявите статический метод внутри нового класса с необходимыми аргументами и установите Matcher <View> в качестве возвращаемого типа.

Объявите статический метод внутри нового класса с необходимыми аргументами и установите Matcher <View> в качестве возвращаемого типа.

public final class MyMatchers {
   @NonNull
   public static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
   }
}
  • Создайте новый объект BoundedMatcher (также возвращаемое значение) с подписью ниже внутри статического метода,

Создайте новый объект BoundedMatcher (также возвращаемое значение) с подписью ниже внутри статического метода,

public final class MyMatchers {
   @NonNull
   public static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
      return new BoundedMatcher<View, TextView>(TextView.class) {
      };
   }
}
  • Переопределите методы descriptionTo и matchSafely в объекте BoundedMatcher . descriptionTo имеет единственный аргумент типа Description без возвращаемого типа и используется для получения информации об ошибках, связанных с сопоставителями. matchSafely имеет единственный аргумент типа TextView с возвращаемым типом boolean, и он используется для соответствия представлению.

Переопределите методы descriptionTo и matchSafely в объекте BoundedMatcher . descriptionTo имеет единственный аргумент типа Description без возвращаемого типа и используется для получения информации об ошибках, связанных с сопоставителями. matchSafely имеет единственный аргумент типа TextView с возвращаемым типом boolean, и он используется для соответствия представлению.

Окончательная версия кода выглядит следующим образом:

public final class MyMatchers {
   @NonNull
   public static Matcher<View> withIdAndText(final Matcher<Integer>
   integerMatcher, final Matcher<String> stringMatcher) {
      return new BoundedMatcher<View, TextView>(TextView.class) {
         @Override
         public void describeTo(final Description description) {
            description.appendText("error text: ");
            stringMatcher.describeTo(description);
            integerMatcher.describeTo(description);
         }
         @Override
         public boolean matchesSafely(final TextView textView) {
            return stringMatcher.matches(textView.getText().toString()) &&
            integerMatcher.matches(textView.getId());
         }
      };
   }
}
  • Наконец, мы можем использовать наш mew matcher для написания тестового примера, как показано ниже,

Наконец, мы можем использовать наш mew matcher для написания тестового примера, как показано ниже,

@Test
public void view_customMatcher_isCorrect() {
   onView(withIdAndText(is((Integer) R.id.textView_hello), is((String) "Hello World!")))
      .check(matches(withText("Hello World!")));
}

Espresso Testing Framework — Просмотр утверждений

Как обсуждалось ранее, утверждение представления используется для подтверждения того, что как фактическое представление (найденное с использованием сопоставлений представлений), так и ожидаемые представления являются одинаковыми. Пример кода выглядит следующим образом:

onView(withId(R.id.my_view)) .check(matches(withText("Hello")))

Вот,

  • onView () возвращает объект ViewInstruction, соответствующий сопоставленному виду. ViewInteraction используется для взаимодействия с согласованным представлением.

  • withId (R.id.my_view) возвращает сопоставление представления, которое будет соответствовать представлению (фактическому), имеющему атрибуты id, равные my_view .

  • withText («Hello») также возвращает сопоставление представления, которое будет соответствовать представлению (ожидаемому), имеющему текстовые атрибуты, равные Hello .

  • check — это метод, который принимает аргумент типа ViewAssertion и делает утверждение, используя переданный в объекте ViewAssertion .

  • match (withText («Hello»)) возвращает утверждение представления, которое выполнит реальную работу по утверждению, что как фактическое представление (найденное с использованием withId ), так и ожидаемое представление (найденное с использованием withText ) — одно и то же.

onView () возвращает объект ViewInstruction, соответствующий сопоставленному виду. ViewInteraction используется для взаимодействия с согласованным представлением.

withId (R.id.my_view) возвращает сопоставление представления, которое будет соответствовать представлению (фактическому), имеющему атрибуты id, равные my_view .

withText («Hello») также возвращает сопоставление представления, которое будет соответствовать представлению (ожидаемому), имеющему текстовые атрибуты, равные Hello .

check — это метод, который принимает аргумент типа ViewAssertion и делает утверждение, используя переданный в объекте ViewAssertion .

match (withText («Hello»)) возвращает утверждение представления, которое выполнит реальную работу по утверждению, что как фактическое представление (найденное с использованием withId ), так и ожидаемое представление (найденное с использованием withText ) — одно и то же.

Давайте изучим некоторые методы, предоставляемые средой тестирования эспрессо для утверждения объектов представления.

не существует()

Возвращает утверждение представления, которое гарантирует, что сопоставитель представления не найдет подходящего представления.

onView(withText("Hello")) .check(doesNotExist());

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

Матчи()

Принимает целевое сопоставление представления и возвращает утверждение представления, которое гарантирует, что сопоставление представления (фактическое) существует и совпадает с представлением, сопоставленным сопоставителем представления цели.

onView(withId(R.id.textView_hello)) .check(matches(withText("Hello World!")));

Здесь тестовый пример гарантирует, что представление с идентификатором R.id.textView_hello существует и соответствует целевому представлению с текстом Hello World!

isBottomAlignedWith ()

Принимает сопоставление вида цели и возвращает утверждение вида, которое гарантирует, что сопоставление вида (фактическое) существует и выровнено снизу с сопоставителем вида цели.

onView(withId(R.id.view)) .check(isBottomAlignedWith(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и выровнено снизу с представлением, имеющим идентификатор R.id.target_view .

isCompletelyAbove ()

Принимает целевое сопоставление вида и возвращает утверждение вида, которое гарантирует, что сопоставление вида (фактическое) существует и полностью расположено над целевым сопоставлением вида.

onView(withId(R.id.view)) .check(isCompletelyAbove(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и располагается полностью над представлением, имеющим идентификатор R.id.target_view.

isCompletelyBelow ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и находится полностью ниже целевого сопоставления вида.

onView(withId(R.id.view)) .check(isCompletelyBelow(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и располагается полностью под представлением, имеющим идентификатор R.id.target_view .

isCompletelyLeftOf ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и находится полностью слева от целевого сопоставления вида.

onView(withId(R.id.view)) .check(isCompletelyLeftOf(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление с идентификатором R.id.view существует и позиционируется полностью слева от представления с идентификатором R.id.target_view

isCompletelyRightOf ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и находится полностью справа от целевого сопоставления вида.

onView(withId(R.id.view)) .check(isCompletelyRightOf(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и позиционируется полностью справа от представления, имеющего идентификатор R.id.target_view.

isLeftAlignedWith ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и выровнено по левому краю с сопоставителем целевого представления.

onView(withId(R.id.view)) .check(isLeftAlignedWith(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление с идентификатором R.id.view существует и выровнено по левому краю с представлением с идентификатором R.id.target_view

isPartiallyAbove ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и находится частично над целевым сопоставлением вида.

onView(withId(R.id.view)) .check(isPartiallyAbove(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и находится частично над представлением, имеющим идентификатор R.id.target_view.

isPartiallyBelow ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и находится частично ниже целевого сопоставления вида.

onView(withId(R.id.view)) .check(isPartiallyBelow(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и находится частично ниже представления, имеющего идентификатор R.id.target_view .

isPartiallyLeftOf ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и находится частично слева от целевого сопоставления вида.

onView(withId(R.id.view)) .check(isPartiallyLeftOf(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и располагается частично слева от представления, имеющего идентификатор R.id.target_view .

isPartiallyRightOf ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и находится частично справа от целевого сопоставления вида

onView(withId(R.id.view)) .check(isPartiallyRightOf(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление, имеющее идентификатор R.id.view, существует и частично позиционируется справа от представления, имеющего идентификатор R.id.target_view .

isRightAlignedWith ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и выровнено по правому краю с сопоставителем целевого представления.

onView(withId(R.id.view)) .check(isRightAlignedWith(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление с идентификатором R.id.view существует и выровнено по правому краю с представлением с идентификатором R.id.target_view .

isTopAlignedWith ()

Принимает целевое сопоставление вида и возвращает утверждение представления, которое гарантирует, что сопоставление вида (фактическое) существует и выровнено по верху с целевым сопоставлением представления.

onView(withId(R.id.view)) .check(isTopAlignedWith(withId(R.id.target_view)))

Здесь тестовый пример гарантирует, что представление с идентификатором R.id.view существует и выровнено по верху с представлением с идентификатором R.id.target_view

noEllipsizedText ()

Возвращает утверждение представления, которое гарантирует, что иерархия представления не содержит эллиптические или отрезанные текстовые представления.

onView(withId(R.id.view)) .check(noEllipsizedText());

noMultilineButtons ()

Возвращает утверждение представления, которое гарантирует, что иерархия представления не содержит многострочных кнопок.

onView(withId(R.id.view)) .check(noMultilineButtons());

noOverlaps ()

Возвращает утверждение представления, которое гарантирует, что объект-потомок, назначаемый TextView или ImageView, не перекрывает друг друга. У него есть другая опция, которая принимает сопоставление целевого представления и возвращает утверждение представления, которое гарантирует, что дочернее представление, соответствующее целевому представлению, не перекрывается.

Espresso Testing Framework — Просмотреть действия

Как выяснилось ранее, просмотр действий автоматизирует все возможные действия, выполняемые пользователями в приложении для Android. Espresso onView и «onData» предоставляют метод execute, который принимает действия просмотра и вызывает / автоматизирует соответствующие действия пользователя в тестовой среде. Например, «click ()» — это действие представления, которое при передаче методу onView ( R.id.myButton ) .perform (click ()) инициирует событие нажатия кнопки (с идентификатором «myButton»). ) в тестовой среде.

В этой главе мы узнаем о действиях просмотра, предоставляемых средой тестирования эспрессо.

TypeText ()

typeText () принимает один аргумент (текст) типа String и возвращает действие представления. Возвращенное действие представления вводит предоставленный текст в представление. Перед размещением текста он касается вида один раз. Контент может быть размещен в произвольной позиции, если он уже содержит текст.

onView(withId(R.id.text_view)).perform(typeText("Hello World!"))

typeTextIntoFocusedView ()

typeTextIntoFocusedView () аналогичен typeText () за исключением того, что он помещает текст прямо рядом с позицией курсора в представлении.

onView(withId(R.id.text_view)).perform(typeTextIntoFocusedView("Hello World!"))

ReplaceText ()

replaceText () аналогична typeText () за исключением того, что она заменяет содержимое представления.

onView(withId(R.id.text_view)).perform(typeTextIntoFocusedView("Hello World!"))

ClearText ()

clearText () не имеет аргументов и возвращает действие представления, которое очистит текст в представлении.

onView(withId(R.id.text_view)).perform(clearText())

pressKey ()

pressKey () принимает код клавиши (например, KeyEvent.KEYCODE_ENTER) и возвращает действие просмотра, при котором нажатие клавиши соответствует коду клавиши.

onView(withId(R.id.text_view)).perform(typeText(
   "Hello World!", pressKey(KeyEvent.KEYCODE_ENTER))

pressMenuKey ()

pressMenuKey () не имеет аргументов и возвращает действие просмотра, при котором будет нажата клавиша аппаратного меню.

onView(withId(R.id.text_view)).perform(typeText(
   "Hello World!", pressKey(KeyEvent.KEYCODE_ENTER), pressMenuKey())

closeSoftKeyboard ()

closeSoftKeyboard () не имеет аргументов и возвращает действие просмотра, которое закроет клавиатуру, если она открыта.

onView(withId(R.id.text_view)).perform(typeText(
   "Hello World!", closeSoftKeyboard())

нажмите ()

click () не имеет аргументов и возвращает действие представления, которое вызовет действие щелчка представления.

onView(withId(R.id.button)).perform(click())

двойной щелчок()

doubleClick () не имеет аргументов и возвращает действие представления, которое вызовет действие представления двойного щелчка.

onView(withId(R.id.button)).perform(doubleClick())

longClick ()

longClick () не имеет аргументов и возвращает действие представления, которое вызовет действие представления с длинным щелчком.

onView(withId(R.id.button)).perform(longClick())

pressBack ()

pressBack () не имеет аргументов и возвращает действие просмотра, при котором будет нажата кнопка «Назад».

onView(withId(R.id.button)).perform(pressBack())

pressBackUnconditionally ()

Функция pressBackUnconditional () не имеет аргументов и возвращает действие просмотра, которое щелкает кнопку «Назад» и не выдает исключение, если действие кнопки «Назад» выходит из самого приложения.

onView(withId(R.id.button)).perform(pressBack())

открыть ссылку()

openLink () имеет два аргумента. Первый аргумент (текст ссылки) имеет тип Matcher и ссылается на текст тега привязки HTML. Второй аргумент (url) имеет тип Matcher и ссылается на URL тега привязки HTML. Это применимо только для TextView . Он возвращает действие представления, которое собирает все теги привязки HTML, доступные в содержимом текстового представления, находит тег привязки, соответствующий первому аргументу (текст ссылки) и второму аргументу (URL), и, наконец, открывает соответствующий URL. Давайте рассмотрим текстовое представление с содержанием как —

<a href="http://www.google.com/">copyright</a>

Затем ссылку можно открыть и протестировать с помощью приведенного ниже теста.

onView(withId(R.id.text_view)).perform(openLink(is("copyright"),
   is(Uri.parse("http://www.google.com/"))))

Здесь openLink получает содержимое текстового представления, находит ссылку с авторским правом в виде текста, www.google.com — в качестве URL-адреса и открывает URL-адрес в браузере.

openLinkWithText ()

openLinkWithText () имеет один аргумент, который может иметь тип ** String * или Matcher. Это просто короткий путь к методу openLink *.

onView(withId(R.id.text_view)).perform(openLinkWithText("copyright"))

openLinkWithUri ()

openLinkWithUri () имеет один аргумент, который может иметь тип String или Matcher. Это просто короткий путь к методу openLink *.

onView(withId(R.id.text_view)).perform(openLinkWithUri("http://www.google.com/"))

pressImeActionButton ()

pressImeActionButton () не имеет аргументов и возвращает действие представления, которое выполнит действие, заданное в конфигурации android: imeOptions . Например, если android: imeOptions равен actionNext, это переместит курсор к следующему возможному представлению EditText на экране.

onView(withId(R.id.text_view)).perform(pressImeActionButton())

scrollTo ()

scrollTo () не имеет аргументов и возвращает действие просмотра, которое будет прокручивать совпадающий scrollView на экране.

onView(withId(R.id.scrollView)).perform(scrollTo())

проведите вниз()

swipeDown () не имеет аргументов и возвращает действие просмотра, которое вызовет действие смахивания вниз на экране.

onView(withId(R.id.root)).perform(swipeDown())

swipeUp ()

Функция swipeUp () не имеет аргументов и возвращает действие просмотра, которое запустит действие смахивания вверх на экране.

onView(withId(R.id.root)).perform(swipeUp())

swipeRight ()

swipeRight () не имеет аргументов и возвращает действие просмотра, которое вызовет действие « Провести пальцем» по экрану.

onView(withId(R.id.root)).perform(swipeRight())

проведите пальцем влево()

swipeLeft () не имеет аргументов и возвращает действие просмотра, которое вызовет левое действие на экране.

onView(withId(R.id.root)).perform(swipeLeft())

Платформа для эспрессо-тестирования — AdapterView

AdapterView — это особый вид представления, специально разработанный для отображения набора аналогичной информации, такой как список продуктов и пользовательские контакты, полученные из основного источника данных с использованием Adapter . Источником данных может быть простой список сложных записей в базе данных. Некоторые из представлений, полученных из AdapterView, это ListView , GridView и Spinner .

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

После анализа природа архитектуры AdapterView делает опцию onView и сопоставления его представлений неактуальной, поскольку конкретный проверяемый вид может вообще не отображаться вообще. К счастью, espresso предоставляет метод onData ( ), который принимает сопоставления hamcrest (относящиеся к типу данных базовых данных) для сопоставления базовых данных и возвращает объект типа DataInteraction, соответствующий представлению сопоставленных данных. Пример кода выглядит следующим образом:

onData(allOf(is(instanceOf(String.class)), startsWith("Apple"))).perform(click())

Здесь onData () сопоставляет запись «Apple», если она доступна в базовых данных (список массивов), и возвращает объект DataInteraction для взаимодействия с согласованным представлением (TextView, соответствующее записи «Apple»).

методы

DataInteraction предоставляет следующие методы для взаимодействия с представлением,

выполнить ()

Это принимает действия просмотра и запускает переданные действия просмотра.

onData(allOf(is(instanceOf(String.class)), startsWith("Apple"))).perform(click())

проверять()

Это принимает утверждения представления и проверяет переданные утверждения представления.

onData(allOf(is(instanceOf(String.class)), startsWith("Apple")))
   .check(matches(withText("Apple")))

inAdapterView ()

Это принимает представления соответствия. Он выбирает конкретный AdapterView на основе переданных сопоставлений представления и возвращает объект DataInteraction для взаимодействия с сопоставленным AdapterView.

onData(allOf())
   .inAdapterView(withId(R.id.adapter_view))
   .atPosition(5)
   .perform(click())

atPosition ()

Это принимает аргумент типа integer и ссылается на позицию элемента в базовых данных. Он выбирает представление, соответствующее переданному позиционному значению данных, и возвращает объект DataInteraction для взаимодействия с сопоставленным представлением. Это будет полезно, если мы знаем правильный порядок основных данных.

onData(allOf())
   .inAdapterView(withId(R.id.adapter_view))
   .atPosition(5)
   .perform(click())

onChildView ()

Это принимает сопоставления представлений и сопоставляет представление внутри определенного дочернего представления. Например, мы можем взаимодействовать с определенными элементами, такими как кнопка « Купить» в списке продуктов на основе AdapterView .

onData(allOf(is(instanceOf(String.class)), startsWith("Apple")))
   .onChildView(withId(R.id.buy_button))
   .perform(click())

Написать образец заявки

Выполните шаги, показанные ниже, чтобы написать простое приложение на основе AdapterView и написать контрольный пример с помощью метода onData () .

  • Запустите Android-студию.

  • Создайте новый проект, как обсуждалось ранее, и назовите его MyFruitApp .

  • Перенесите приложение на платформу AndroidX, используя меню параметров RefactorMigrate to AndroidX .

  • Удалите дизайн по умолчанию в основной деятельности и добавьте ListView . Содержимое Activity_main.xml выглядит следующим образом:

Запустите Android-студию.

Создайте новый проект, как обсуждалось ранее, и назовите его MyFruitApp .

Перенесите приложение на платформу AndroidX, используя меню параметров RefactorMigrate to AndroidX .

Удалите дизайн по умолчанию в основной деятельности и добавьте ListView . Содержимое Activity_main.xml выглядит следующим образом:

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   xmlns:app = "http://schemas.android.com/apk/res-auto"
   xmlns:tools = "http://schemas.android.com/tools"
   android:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <ListView
      android:id = "@+id/listView"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content" />
</RelativeLayout>
  • Добавьте новый ресурс макета item.xml, чтобы указать шаблон элемента представления списка. Содержимое item.xml выглядит следующим образом:

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

<?xml version = "1.0" encoding = "utf-8"?>
<TextView xmlns:android = "http://schemas.android.com/apk/res/android"
   android:id = "@+id/name"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent"
   android:padding = "8dp"
/>
  • Теперь создайте адаптер с массивом фруктов в качестве базовых данных и установите его в виде списка. Это должно быть сделано в onCreate () MainActivity, как указано ниже,

Теперь создайте адаптер с массивом фруктов в качестве базовых данных и установите его в виде списка. Это должно быть сделано в onCreate () MainActivity, как указано ниже,

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   
   // Find fruit list view
   final ListView listView = (ListView) findViewById(R.id.listView);
   
   // Initialize fruit data
   String[] fruits = new String[]{
      "Apple", 
      "Banana", 
      "Cherry", 
      "Dates", 
      "Elderberry", 
      "Fig", 
      "Grapes", 
      "Grapefruit", 
      "Guava",
      "Jack fruit", 
      "Lemon",
      "Mango", 
      "Orange", 
      "Papaya", 
      "Pears", 
      "Peaches", 
      "Pineapple",
      "Plums", 
      "Raspberry",
      "Strawberry", 
      "Watermelon"
   };
   
   // Create array list of fruits
   final ArrayList<String> fruitList = new ArrayList<String>();
   for (int i = 0; i < fruits.length; ++i) {
      fruitList.add(fruits[i]);
   }
   
   // Create Array adapter
   final ArrayAdapter adapter = new ArrayAdapter(this, R.layout.item, fruitList);
   
   // Set adapter in list view
   listView.setAdapter(adapter);
}
  • Теперь скомпилируйте код и запустите приложение. Снимок экрана приложения My Fruit выглядит следующим образом:

Теперь скомпилируйте код и запустите приложение. Снимок экрана приложения My Fruit выглядит следующим образом:

Скомпилируйте код

  • Теперь откройте файл ExampleInstrumentedTest.java и добавьте ActivityTestRule, как указано ниже,

Теперь откройте файл ExampleInstrumentedTest.java и добавьте ActivityTestRule, как указано ниже,

@Rule
public ActivityTestRule<MainActivity> mActivityRule =
   new ActivityTestRule<MainActivity>(MainActivity.class);

Также убедитесь, что тестовая конфигурация выполнена в app / build.gradle

dependencies {
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'androidx.test:runner:1.1.1'
   androidTestImplementation 'androidx.test:rules:1.1.1'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
  • Добавьте новый контрольный пример для проверки представления списка, как показано ниже,

Добавьте новый контрольный пример для проверки представления списка, как показано ниже,

@Test
public void listView_isCorrect() {
   // check list view is visible
   onView(withId(R.id.listView)).check(matches(isDisplayed()));
   onData(allOf(is(instanceOf(String.class)), startsWith("Apple"))).perform(click());
   onData(allOf(is(instanceOf(String.class)), startsWith("Apple")))
      .check(matches(withText("Apple")));
   // click a child item
   onData(allOf())
      .inAdapterView(withId(R.id.listView))
      .atPosition(10)
      .perform(click());
}
  • Наконец, запустите тестовый набор, используя контекстное меню Android Studio, и проверьте, все ли тестовые примеры выполнены успешно.

Наконец, запустите тестовый набор, используя контекстное меню Android Studio, и проверьте, все ли тестовые примеры выполнены успешно.

Espresso Testing Framework — WebView

WebView — это специальный вид, предоставляемый Android для отображения веб-страниц внутри приложения. WebView не предоставляет все функции полноценного браузера, такого как Chrome и Firefox. Тем не менее, он обеспечивает полный контроль над отображаемым содержимым и предоставляет все функции Android, которые должны вызываться на веб-страницах. Он включает WebView и предоставляет специальную среду, в которой пользовательский интерфейс может быть легко спроектирован с использованием технологии HTML и встроенных функций, таких как камера и набор номера. Этот набор функций позволяет WebView предоставлять приложение нового типа, называемое гибридным приложением , где пользовательский интерфейс выполняется в HTML, а бизнес-логика — в JavaScript или через внешнюю конечную точку API.

Как правило, тестирование WebView должно быть сложной задачей, поскольку он использует технологию HTML для своих элементов пользовательского интерфейса, а не собственного пользовательского интерфейса / представлений. Эспрессо выделяется в этой области, предоставляя новый набор для веб-сопоставлений и веб-утверждений, который намеренно похож на нативные сопоставления представлений и утверждений представлений. В то же время он обеспечивает сбалансированный подход, включая также среду тестирования на основе веб-технологий.

Espresso web построен на платформе WebDriver Atom, которая используется для поиска и управления веб-элементами. Атом похож на просмотр действий. Атом будет делать все взаимодействие внутри веб-страницы. WebDriver предоставляет предопределенный набор методов, таких как findElement () , getElement () для поиска веб-элементов и возвращает соответствующие атомы (для выполнения действий на веб-странице).

Стандартное заявление о веб-тестировании выглядит следующим образом:

onWebView()
   .withElement(Atom)
   .perform(Atom)
   .check(WebAssertion)

Вот,

  • onWebView () — Подобно onView (), он предоставляет набор API для тестирования WebView.

  • withElement () — один из нескольких методов, используемых для поиска веб-элементов внутри веб-страницы с использованием Atom и возврата объекта WebInstruction, который аналогичен ViewInteraction.

  • execute () — выполняет действие внутри веб-страницы с помощью Atom и возвращает WebInteraction.

  • check () — это делает необходимое утверждение с помощью WebAssertion.

onWebView () — Подобно onView (), он предоставляет набор API для тестирования WebView.

withElement () — один из нескольких методов, используемых для поиска веб-элементов внутри веб-страницы с использованием Atom и возврата объекта WebInstruction, который аналогичен ViewInteraction.

execute () — выполняет действие внутри веб-страницы с помощью Atom и возвращает WebInteraction.

check () — это делает необходимое утверждение с помощью WebAssertion.

Пример кода веб-тестирования выглядит следующим образом:

onWebView()
   .withElement(findElement(Locator.ID, "apple"))
   .check(webMatches(getText(), containsString("Apple")))

Вот,

  • findElement () находит элемент и возвращает атом

  • webMatches похож на метод совпадений

findElement () находит элемент и возвращает атом

webMatches похож на метод совпадений

Написать образец заявки

Давайте напишем простое приложение на основе WebView и напишем тестовый пример с помощью метода onWebView () . Выполните следующие шаги, чтобы написать пример приложения —

  • Запустите Android-студию.

  • Создайте новый проект, как обсуждалось ранее, и назовите его MyWebViewApp .

  • Перенесите приложение на платформу AndroidX, используя меню параметров RefactorMigrate to AndroidX .

  • Добавьте приведенный ниже параметр конфигурации в файл AndroidManifest.xml, чтобы дать разрешение на доступ к Интернету.

Запустите Android-студию.

Создайте новый проект, как обсуждалось ранее, и назовите его MyWebViewApp .

Перенесите приложение на платформу AndroidX, используя меню параметров RefactorMigrate to AndroidX .

Добавьте приведенный ниже параметр конфигурации в файл AndroidManifest.xml, чтобы дать разрешение на доступ к Интернету.

<uses-permission android:name = "android.permission.INTERNET" />
  • Espresso web предоставляется в виде отдельного плагина. Итак, добавьте зависимость в app / build.gradle и синхронизируйте ее.

Espresso web предоставляется в виде отдельного плагина. Итак, добавьте зависимость в app / build.gradle и синхронизируйте ее.

dependencies {
   androidTestImplementation 'androidx.test:rules:1.1.1'
   androidTestImplementation 'androidx.test.espresso:espresso-web:3.1.1'
}
  • Удалите дизайн по умолчанию в основной деятельности и добавьте WebView. Содержимое Activity_main.xml выглядит следующим образом:

Удалите дизайн по умолчанию в основной деятельности и добавьте WebView. Содержимое Activity_main.xml выглядит следующим образом:

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   xmlns:app = "http://schemas.android.com/apk/res-auto"
   xmlns:tools = "http://schemas.android.com/tools"
   android:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <WebView
      android:id = "@+id/web_view_test"
      android:layout_width = "fill_parent"
      android:layout_height = "fill_parent" />
</RelativeLayout>
  • Создайте новый класс ExtendedWebViewClient, расширяющий WebViewClient, и переопределите метод shouldOverrideUrlLoading для загрузки действия ссылки в том же WebView ; в противном случае откроется новое окно браузера вне приложения. Поместите его в MainActivity.java .

Создайте новый класс ExtendedWebViewClient, расширяющий WebViewClient, и переопределите метод shouldOverrideUrlLoading для загрузки действия ссылки в том же WebView ; в противном случае откроется новое окно браузера вне приложения. Поместите его в MainActivity.java .

private class ExtendedWebViewClient extends WebViewClient {
   @Override
   public boolean shouldOverrideUrlLoading(WebView view, String url) {
      view.loadUrl(url);
      return true;
   }
}
  • Теперь добавьте приведенный ниже код в метод onCreate MainActivity . Цель кода — найти WebView , правильно его настроить и затем, наконец, загрузить целевой URL.

Теперь добавьте приведенный ниже код в метод onCreate MainActivity . Цель кода — найти WebView , правильно его настроить и затем, наконец, загрузить целевой URL.

// Find web view
WebView webView = (WebView) findViewById(R.id.web_view_test);

// set web view client
webView.setWebViewClient(new ExtendedWebViewClient());

// Clear cache
webView.clearCache(true);

// load Url
webView.loadUrl("http://<your domain or IP>/index.html");

Вот,

  • Содержание файла index.html выглядит следующим образом:

Содержание файла index.html выглядит следующим образом:

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   <body>
      <h1>Fruits</h1>
      <ol>
         <li><a href = "apple.html" id = "apple">Apple</a></li>
         <li><a href = "banana.html" id = "banana">Banana</a></li>
         </ol>
   </body>
</html>
  • Содержимое файла apple.html, указанного в index.html, выглядит следующим образом:

Содержимое файла apple.html, указанного в index.html, выглядит следующим образом:

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   
   <body>
      <h1>Apple</h1>
   </body>
</html>
  • Содержимое файла banana.html, указанного в banana.html, выглядит следующим образом:

Содержимое файла banana.html, указанного в banana.html, выглядит следующим образом:

<html>
   <head>
      <title>Android Web View Sample</title>
   </head>
   
   <body>
      <h1>Banana</h1>
   </body>
</html>
  • Поместите index.html, apple.html и banana.html на веб-сервер

  • Замените URL-адрес в методе loadUrl вашим настроенным URL-адресом.

  • Теперь запустите приложение и вручную проверьте, все ли в порядке. Ниже приведен скриншот примера приложения WebView.

Поместите index.html, apple.html и banana.html на веб-сервер

Замените URL-адрес в методе loadUrl вашим настроенным URL-адресом.

Теперь запустите приложение и вручную проверьте, все ли в порядке. Ниже приведен скриншот примера приложения WebView.

Пример WebView

  • Теперь откройте файл ExampleInstrumentedTest.java и добавьте следующее правило:

Теперь откройте файл ExampleInstrumentedTest.java и добавьте следующее правило:

@Rule
public ActivityTestRule<MainActivity> mActivityRule =
   new ActivityTestRule<MainActivity>(MainActivity.class, false, true) {
   @Override
   protected void afterActivityLaunched() {
      onWebView(withId(R.id.web_view_test)).forceJavascriptEnabled();
   }
};

Здесь мы нашли WebView и включили JavaScript WebView, потому что среда веб-тестирования эспрессо работает исключительно через движок JavaScript для идентификации и управления веб-элементом.

  • Теперь добавьте контрольный пример, чтобы проверить наш WebView и его поведение.

Теперь добавьте контрольный пример, чтобы проверить наш WebView и его поведение.

@Test
public void webViewTest(){
   onWebView()
      .withElement(findElement(Locator.ID, "apple"))
      .check(webMatches(getText(), containsString("Apple")))
      .perform(webClick())
      .withElement(findElement(Locator.TAG_NAME, "h1"))
      .check(webMatches(getText(), containsString("Apple")));
}

Здесь тестирование проводилось в следующем порядке,

  • нашел ссылку, яблоко, используя его атрибут id через метод findElement () и перечисление Locator.ID .

  • проверяет текст ссылки с помощью метода webMatches ()

  • выполняет действие клика по ссылке. Откроется страница apple.html .

  • снова нашел элемент h1, используя методы findElement () и перечисление Locator.TAG_NAME .

  • Наконец, снова проверяется текст тега h1 с помощью метода webMatches () .

  • Наконец, запустите тестовый пример, используя контекстное меню Android Studio.

нашел ссылку, яблоко, используя его атрибут id через метод findElement () и перечисление Locator.ID .

проверяет текст ссылки с помощью метода webMatches ()

выполняет действие клика по ссылке. Откроется страница apple.html .

снова нашел элемент h1, используя методы findElement () и перечисление Locator.TAG_NAME .

Наконец, снова проверяется текст тега h1 с помощью метода webMatches () .

Наконец, запустите тестовый пример, используя контекстное меню Android Studio.

Асинхронные операции

В этой главе мы узнаем, как тестировать асинхронные операции, используя Espresso Idling Resources.

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

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

Пользовательский интерфейс Threading

Он внутренне используется Android SDK для обеспечения удобного взаимодействия со сложными элементами пользовательского интерфейса. Эспрессо поддерживает этот сценарий прозрачно и не требует никакой настройки и специального кодирования.

Асинхронная задача

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

Пользовательская тема

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

Давайте рассмотрим концепцию ресурса холостого хода и как это сделать в этой главе.

обзор

Концепция ресурса холостого хода очень проста и интуитивно понятна. Основная идея заключается в создании переменной (логическое значение) всякий раз, когда в отдельном потоке запускается длительный процесс, чтобы определить, запущен ли процесс или нет, и зарегистрировать его в среде тестирования. Во время тестирования тестовый исполнитель проверит зарегистрированную переменную, если таковая найдена, а затем найдет ее состояние выполнения. Если текущее состояние равно true, тестовый исполнитель будет ждать, пока статус станет ложным.

Espresso предоставляет интерфейс IdlingResources с целью поддержания рабочего состояния. Основным методом реализации является isIdleNow (). Если isIdleNow () возвращает true, espresso возобновит процесс тестирования или подождет, пока isIdleNow () вернет false. Нам нужно реализовать IdlingResources и использовать производный класс. Espresso также предоставляет некоторые встроенные реализации IdlingResources для облегчения нашей рабочей нагрузки. Они заключаются в следующем,

CountingIdlingResource

Это поддерживает внутренний счетчик запущенной задачи. Он предоставляет методы increment () и decment () . increment () добавляет единицу к счетчику, а increment () удаляет один из счетчика. isIdleNow () возвращает true только тогда, когда ни одна задача не активна.

UriIdlingResource

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

IdlingThreadPoolExecutor

Это пользовательская реализация ThreadPoolExecutor для поддержания числа активных запущенных задач в текущем пуле потоков.

IdlingScheduledThreadPoolExecutor

Это похоже на IdlingThreadPoolExecutor , но оно также планирует задачу и пользовательскую реализацию ScheduledThreadPoolExecutor.

Если в приложении используется какая-либо из вышеперечисленных реализаций IdlingResources или пользовательская, мы должны зарегистрировать ее в среде тестирования, а также до тестирования приложения с использованием класса IdlingRegistry, как показано ниже:

IdlingRegistry.getInstance().register(MyIdlingResource.getIdlingResource());

Более того, его можно удалить после завершения тестирования, как показано ниже:

IdlingRegistry.getInstance().unregister(MyIdlingResource.getIdlingResource());

Espresso предоставляет эту функциональность в отдельном пакете, и его необходимо настроить, как показано ниже в app.gradle.

dependencies {
   implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'
   androidTestImplementation "androidx.test.espresso.idling:idlingconcurrent:3.1.1"
}

Образец заявки

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

  • Запустите Android-студию.

  • Создайте новый проект, как обсуждалось ранее, и назовите его MyIdlingFruitApp.

  • Перенесите приложение на платформу AndroidX, используя меню параметров Refactor → Migrate to AndroidX .

  • Добавьте библиотеку ресурсов эспрессо на холостом ходу в app / build.gradle (и синхронизируйте ее), как указано ниже,

Запустите Android-студию.

Создайте новый проект, как обсуждалось ранее, и назовите его MyIdlingFruitApp.

Перенесите приложение на платформу AndroidX, используя меню параметров Refactor → Migrate to AndroidX .

Добавьте библиотеку ресурсов эспрессо на холостом ходу в app / build.gradle (и синхронизируйте ее), как указано ниже,

dependencies {
   implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'
   androidTestImplementation "androidx.test.espresso.idling:idlingconcurrent:3.1.1"
}
  • Удалите дизайн по умолчанию в основной деятельности и добавьте ListView. Содержимое Activity_main.xml выглядит следующим образом:

Удалите дизайн по умолчанию в основной деятельности и добавьте ListView. Содержимое Activity_main.xml выглядит следующим образом:

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   xmlns:app = "http://schemas.android.com/apk/res-auto"
   xmlns:tools = "http://schemas.android.com/tools"
   android:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <ListView
      android:id = "@+id/listView"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content" />
</RelativeLayout>
  • Добавьте новый ресурс макета item.xml, чтобы указать шаблон элемента представления списка. Содержимое item.xml выглядит следующим образом:

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

<?xml version = "1.0" encoding = "utf-8"?>
<TextView xmlns:android = "http://schemas.android.com/apk/res/android"
   android:id = "@+id/name"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent"
   android:padding = "8dp"
/>
  • Создайте новый класс — MyIdlingResource . MyIdlingResource используется для хранения нашего IdlingResource в одном месте и извлечения его при необходимости. Мы собираемся использовать CountingIdlingResource в нашем примере.

Создайте новый класс — MyIdlingResource . MyIdlingResource используется для хранения нашего IdlingResource в одном месте и извлечения его при необходимости. Мы собираемся использовать CountingIdlingResource в нашем примере.

package com.tutorialspoint.espressosamples.myidlingfruitapp;
import androidx.test.espresso.IdlingResource;
import androidx.test.espresso.idling.CountingIdlingResource;

public class MyIdlingResource {
   private static CountingIdlingResource mCountingIdlingResource =
      new CountingIdlingResource("my_idling_resource");
   public static void increment() {
      mCountingIdlingResource.increment();
   }
   public static void decrement() {
      mCountingIdlingResource.decrement();
   }
   public static IdlingResource getIdlingResource() {
      return mCountingIdlingResource;
   }
}
  • Объявите глобальную переменную mIdlingResource типа CountingIdlingResource в классе MainActivity, как показано ниже,

Объявите глобальную переменную mIdlingResource типа CountingIdlingResource в классе MainActivity, как показано ниже,

@Nullable
private CountingIdlingResource mIdlingResource = null;
  • Напишите приватный метод получения списка фруктов из Интернета, как показано ниже,

Напишите приватный метод получения списка фруктов из Интернета, как показано ниже,

private ArrayList<String> getFruitList(String data) {
   ArrayList<String> fruits = new ArrayList<String>();
   try {
      // Get url from async task and set it into a local variable
      URL url = new URL(data);
      Log.e("URL", url.toString());
      
      // Create new HTTP connection
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      
      // Set HTTP connection method as "Get"
      conn.setRequestMethod("GET");
      
      // Do a http request and get the response code
      int responseCode = conn.getResponseCode();
      
      // check the response code and if success, get response content
      if (responseCode == HttpURLConnection.HTTP_OK) {
         BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
         String line;
         StringBuffer response = new StringBuffer();
         while ((line = in.readLine()) != null) {
            response.append(line);
         }
         in.close();
         JSONArray jsonArray = new JSONArray(response.toString());
         Log.e("HTTPResponse", response.toString());
         for(int i = 0; i < jsonArray.length(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            String name = String.valueOf(jsonObject.getString("name"));
            fruits.add(name);
         }
      } else {
         throw new IOException("Unable to fetch data from url");
      }
      conn.disconnect();
   } catch (IOException | JSONException e) {
      e.printStackTrace();
   }
   return fruits;
}
  • Создайте новую задачу в методе onCreate () для извлечения данных из Интернета, используя наш метод getFruitList, после чего создайте новый адаптер и установите его в виде списка. Кроме того, уменьшите ресурс холостого хода, как только наша работа будет завершена в потоке. Код выглядит следующим образом:

Создайте новую задачу в методе onCreate () для извлечения данных из Интернета, используя наш метод getFruitList, после чего создайте новый адаптер и установите его в виде списка. Кроме того, уменьшите ресурс холостого хода, как только наша работа будет завершена в потоке. Код выглядит следующим образом:

// Get data
class FruitTask implements Runnable {
   ListView listView;
   CountingIdlingResource idlingResource;
   FruitTask(CountingIdlingResource idlingRes, ListView listView) {
      this.listView = listView;
      this.idlingResource = idlingRes;
   }
   public void run() {
      //code to do the HTTP request
      final ArrayList<String> fruitList = getFruitList("http://<your domain or IP>/fruits.json");
      try {
         synchronized (this){
            runOnUiThread(new Runnable() {
               @Override
               public void run() {
                  // Create adapter and set it to list view
                  final ArrayAdapter adapter = new
                     ArrayAdapter(MainActivity.this, R.layout.item, fruitList);
                  ListView listView = (ListView)findViewById(R.id.listView);
                  listView.setAdapter(adapter);
               }
            });
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (!MyIdlingResource.getIdlingResource().isIdleNow()) {
         MyIdlingResource.decrement(); // Set app as idle.
      }
   }
}

Здесь URL-адрес плода считается http: // <ваш домен или IP / fruits.json и форматируется как JSON. Содержание выглядит следующим образом,

[ 
   {
      "name":"Apple"
   },
   {
      "name":"Banana"
   },
   {
      "name":"Cherry"
   },
   {
      "name":"Dates"
   },
   {
      "name":"Elderberry"
   },
   {
      "name":"Fig"
   },
   {
      "name":"Grapes"
   },
   {
      "name":"Grapefruit"
   },
   {
      "name":"Guava"
   },
   {
      "name":"Jack fruit"
   },
   {
      "name":"Lemon"
   },
   {
      "name":"Mango"
   },
   {
      "name":"Orange"
   },
   {
      "name":"Papaya"
   },
   {
      "name":"Pears"
   },
   {
      "name":"Peaches"
   },
   {
      "name":"Pineapple"
   },
   {
      "name":"Plums"
   },
   {
      "name":"Raspberry"
   },
   {
      "name":"Strawberry"
   },
   {
      "name":"Watermelon"
   }
]

Примечание. Поместите файл на локальный веб-сервер и используйте его.

  • Теперь найдите представление, создайте новый поток, передав FruitTask , увеличьте ресурс холостого хода и, наконец, запустите задачу.

Теперь найдите представление, создайте новый поток, передав FruitTask , увеличьте ресурс холостого хода и, наконец, запустите задачу.

// Find list view
ListView listView = (ListView) findViewById(R.id.listView);
Thread fruitTask = new Thread(new FruitTask(this.mIdlingResource, listView));
MyIdlingResource.increment();
fruitTask.start();
  • Полный код MainActivity выглядит следующим образом:

Полный код MainActivity выглядит следующим образом:

package com.tutorialspoint.espressosamples.myidlingfruitapp;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AppCompatActivity;
import androidx.test.espresso.idling.CountingIdlingResource;

import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
   @Nullable
   private CountingIdlingResource mIdlingResource = null;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      
      // Get data
      class FruitTask implements Runnable {
         ListView listView;
         CountingIdlingResource idlingResource;
         FruitTask(CountingIdlingResource idlingRes, ListView listView) {
            this.listView = listView;
            this.idlingResource = idlingRes;
         }
         public void run() {
            //code to do the HTTP request
            final ArrayList<String> fruitList = getFruitList(
               "http://<yourdomain or IP>/fruits.json");
            try {
               synchronized (this){
                  runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
                        // Create adapter and set it to list view
                        final ArrayAdapter adapter = new ArrayAdapter(
                           MainActivity.this, R.layout.item, fruitList);
                        ListView listView = (ListView) findViewById(R.id.listView);
                        listView.setAdapter(adapter);
                     }
                  });
               }
            } catch (Exception e) {
               e.printStackTrace();
            }
            if (!MyIdlingResource.getIdlingResource().isIdleNow()) {
               MyIdlingResource.decrement(); // Set app as idle.
            }
         }
      }
      // Find list view
      ListView listView = (ListView) findViewById(R.id.listView);
      Thread fruitTask = new Thread(new FruitTask(this.mIdlingResource, listView));
      MyIdlingResource.increment();
      fruitTask.start();
   }
   private ArrayList<String> getFruitList(String data) {
      ArrayList<String> fruits = new ArrayList<String>();
      try {
         // Get url from async task and set it into a local variable
         URL url = new URL(data);
         Log.e("URL", url.toString());
         
         // Create new HTTP connection
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         
         // Set HTTP connection method as "Get"
         conn.setRequestMethod("GET");
         
         // Do a http request and get the response code
         int responseCode = conn.getResponseCode();
         
         // check the response code and if success, get response content
         if (responseCode == HttpURLConnection.HTTP_OK) {
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            StringBuffer response = new StringBuffer();
            while ((line = in.readLine()) != null) {
               response.append(line);
            }
            in.close();
            JSONArray jsonArray = new JSONArray(response.toString());
            Log.e("HTTPResponse", response.toString());
            
            for(int i = 0; i < jsonArray.length(); i++) {
               JSONObject jsonObject = jsonArray.getJSONObject(i);
               String name = String.valueOf(jsonObject.getString("name"));
               fruits.add(name);
            }
         } else {
            throw new IOException("Unable to fetch data from url");
         }
         conn.disconnect();
      } catch (IOException | JSONException e) {
         e.printStackTrace();
      }
      return fruits;
   }
}
  • Теперь добавьте приведенную ниже конфигурацию в файл манифеста приложения, AndroidManifest.xml

Теперь добавьте приведенную ниже конфигурацию в файл манифеста приложения, AndroidManifest.xml

<uses-permission android:name = "android.permission.INTERNET" />
  • Теперь скомпилируйте приведенный выше код и запустите приложение. Снимок экрана приложения My Idling Fruit выглядит следующим образом:

Теперь скомпилируйте приведенный выше код и запустите приложение. Снимок экрана приложения My Idling Fruit выглядит следующим образом:

Idling Fruit App

  • Теперь откройте файл ExampleInstrumentedTest.java и добавьте ActivityTestRule, как указано ниже,

Теперь откройте файл ExampleInstrumentedTest.java и добавьте ActivityTestRule, как указано ниже,

@Rule
public ActivityTestRule<MainActivity> mActivityRule = 
   new ActivityTestRule<MainActivity>(MainActivity.class);
Also, make sure the test configuration is done in app/build.gradle
dependencies {
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'androidx.test:runner:1.1.1'
   androidTestImplementation 'androidx.test:rules:1.1.1'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
   implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'
   androidTestImplementation "androidx.test.espresso.idling:idlingconcurrent:3.1.1"
}
  • Добавьте новый контрольный пример для проверки представления списка, как показано ниже,

Добавьте новый контрольный пример для проверки представления списка, как показано ниже,

@Before
public void registerIdlingResource() {
   IdlingRegistry.getInstance().register(MyIdlingResource.getIdlingResource());
}
@Test
public void contentTest() {
   // click a child item
   onData(allOf())
   .inAdapterView(withId(R.id.listView))
   .atPosition(10)
   .perform(click());
}
@After
public void unregisterIdlingResource() {
   IdlingRegistry.getInstance().unregister(MyIdlingResource.getIdlingResource());
}
  • Наконец, запустите тестовый набор, используя контекстное меню Android Studio, и проверьте, все ли тестовые примеры выполнены успешно.

Наконец, запустите тестовый набор, используя контекстное меню Android Studio, и проверьте, все ли тестовые примеры выполнены успешно.

Espresso Testing Framework — Содержание

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

предназначена

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

намеревающийся

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

Настроить

Espresso поддерживает опцию intent через библиотеку плагинов, и ее необходимо настроить в файле gradle приложения. Вариант конфигурации заключается в следующем,

dependencies {
   // ...
   androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}

предназначена()

Плагин Espresso intent предоставляет специальные средства сопоставления, чтобы проверить, является ли вызванное намерение ожидаемым. Предоставленные сопоставители и их назначение заключаются в следующем:

hasAction

Это принимает действие намерения и возвращает совпадение, которое соответствует указанному намерению.

hasData

Это принимает данные и возвращает сопоставление, которое сопоставляет данные, предоставленные намерению, при его вызове.

toPackage

Это принимает имя пакета намерения и возвращает сопоставление, которое совпадает с именем пакета вызванного намерения.

Теперь давайте создадим новое приложение и протестируем приложение на внешнюю активность, используя предназначенный (), чтобы понять концепцию.

  • Запустите Android-студию.

  • Создайте новый проект, как обсуждалось ранее, и назовите его IntentSampleApp.

  • Перенесите приложение на платформу AndroidX, используя меню параметров Refactor → Migrate to AndroidX .

  • Создайте текстовое поле, кнопку, чтобы открыть список контактов, и еще одну, чтобы набрать звонок, изменив файл activity_main.xml, как показано ниже,

Запустите Android-студию.

Создайте новый проект, как обсуждалось ранее, и назовите его IntentSampleApp.

Перенесите приложение на платформу AndroidX, используя меню параметров Refactor → Migrate to AndroidX .

Создайте текстовое поле, кнопку, чтобы открыть список контактов, и еще одну, чтобы набрать звонок, изменив файл activity_main.xml, как показано ниже,

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   xmlns:app = "http://schemas.android.com/apk/res-auto"
   xmlns:tools = "http://schemas.android.com/tools"
   android:layout_width = "match_parent"
   android:layout_height = "match_parent"
   tools:context = ".MainActivity">
   <EditText
      android:id = "@+id/edit_text_phone_number"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:text = ""
      android:autofillHints = "@string/phone_number"/>
   <Button
      android:id = "@+id/call_contact_button"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:layout_below = "@id/edit_text_phone_number"
      android:text = "@string/call_contact"/>
   <Button
      android:id = "@+id/button"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_centerHorizontal = "true"
      android:layout_below = "@id/call_contact_button"
      android:text = "@string/call"/>
</RelativeLayout>
  • Также добавьте следующий элемент в файл ресурсов strings.xml ,

Также добавьте следующий элемент в файл ресурсов strings.xml ,

<string name = "phone_number">Phone number</string>
<string name = "call">Call</string>
<string name = "call_contact">Select from contact list</string>
  • Теперь добавьте приведенный ниже код в основное действие ( MainActivity.java ) в методе onCreate .

Теперь добавьте приведенный ниже код в основное действие ( MainActivity.java ) в методе onCreate .

public class MainActivity extends AppCompatActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      // ... code
      // Find call from contact button
      Button contactButton = (Button) findViewById(R.id.call_contact_button);
      contactButton.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View view) {
            // Uri uri = Uri.parse("content://contacts");
            Intent contactIntent = new Intent(Intent.ACTION_PICK,
               ContactsContract.Contacts.CONTENT_URI);
            contactIntent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
            startActivityForResult(contactIntent, REQUEST_CODE);
         }
      });
      // Find edit view
      final EditText phoneNumberEditView = (EditText)
         findViewById(R.id.edit_text_phone_number);
      // Find call button
      Button button = (Button) findViewById(R.id.button);
      button.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View view) {
            if(phoneNumberEditView.getText() != null) {
               Uri number = Uri.parse("tel:" + phoneNumberEditView.getText());
               Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
               startActivity(callIntent);
            }
         }
      });
   }
   // ... code
}

Здесь мы запрограммировали кнопку с идентификатором call_contact_button, чтобы открыть список контактов, и кнопку с идентификатором, кнопку, чтобы набрать звонок.

  • Добавьте статическую переменную REQUEST_CODE в класс MainActivity, как показано ниже,

Добавьте статическую переменную REQUEST_CODE в класс MainActivity, как показано ниже,

public class MainActivity extends AppCompatActivity {
   // ...
   private static final int REQUEST_CODE = 1;
   // ...
}
  • Теперь добавьте метод onActivityResult в класс MainActivity, как показано ниже:

Теперь добавьте метод onActivityResult в класс MainActivity, как показано ниже:

public class MainActivity extends AppCompatActivity {
   // ...
   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (requestCode == REQUEST_CODE) {
         if (resultCode == RESULT_OK) {
            // Bundle extras = data.getExtras();
            // String phoneNumber = extras.get("data").toString();
            Uri uri = data.getData();
            Log.e("ACT_RES", uri.toString());
            String[] projection = {
               ContactsContract.CommonDataKinds.Phone.NUMBER, 
               ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME };
            Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
            cursor.moveToFirst();
            
            int numberColumnIndex =
               cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
            String number = cursor.getString(numberColumnIndex);
            
            int nameColumnIndex = cursor.getColumnIndex(
               ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
            String name = cursor.getString(nameColumnIndex);
            Log.d("MAIN_ACTIVITY", "Selected number : " + number +" , name : "+name);
            
            // Find edit view
            final EditText phoneNumberEditView = (EditText)
               findViewById(R.id.edit_text_phone_number);
            phoneNumberEditView.setText(number);
         }
      }
   };
   // ...
}

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

  • Запустите приложение и убедитесь, что все в порядке. Окончательный вид заявки Intent приведен ниже,

Запустите приложение и убедитесь, что все в порядке. Окончательный вид заявки Intent приведен ниже,

Образец заявки

  • Теперь настройте намерение эспрессо в файле gradle приложения, как показано ниже,

Теперь настройте намерение эспрессо в файле gradle приложения, как показано ниже,

dependencies {
   // ...
   androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
}
  • Выберите пункт меню « Синхронизировать сейчас», предоставляемый Android Studio. Это загрузит библиотеку тестирования намерений и настроит ее должным образом.

  • Откройте файл ExampleInstrumentedTest.java и добавьте IntentsTestRule вместо обычно используемого AndroidTestRule . IntentTestRule — это специальное правило для обработки намеренного тестирования.

Выберите пункт меню « Синхронизировать сейчас», предоставляемый Android Studio. Это загрузит библиотеку тестирования намерений и настроит ее должным образом.

Откройте файл ExampleInstrumentedTest.java и добавьте IntentsTestRule вместо обычно используемого AndroidTestRule . IntentTestRule — это специальное правило для обработки намеренного тестирования.

public class ExampleInstrumentedTest {
   // ... code
   @Rule
   public IntentsTestRule<MainActivity> mActivityRule =
   new IntentsTestRule<>(MainActivity.class);
   // ... code
}
  • Добавьте две локальные переменные, чтобы установить тестовый номер телефона и имя пакета номеронабирателя, как показано ниже,

Добавьте две локальные переменные, чтобы установить тестовый номер телефона и имя пакета номеронабирателя, как показано ниже,

public class ExampleInstrumentedTest {
   // ... code
   private static final String PHONE_NUMBER = "1 234-567-890";
   private static final String DIALER_PACKAGE_NAME = "com.google.android.dialer";
   // ... code
}
  • Исправьте проблемы с импортом, используя опцию Alt + Enter, предоставляемую android studio, или включите нижеприведенные операторы импорта,

Исправьте проблемы с импортом, используя опцию Alt + Enter, предоставляемую android studio, или включите нижеприведенные операторы импорта,

import android.content.Context;
import android.content.Intent;

import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasData;
import static androidx.test.espresso.intent.matcher.IntentMatchers.toPackage;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.*;
  • Добавьте приведенный ниже контрольный пример, чтобы проверить, правильно ли вызывается номеронабиратель.

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

public class ExampleInstrumentedTest {
   // ... code
   @Test
   public void validateIntentTest() {
      onView(withId(R.id.edit_text_phone_number))
         .perform(typeText(PHONE_NUMBER), closeSoftKeyboard());
      onView(withId(R.id.button)) .perform(click());
      intended(allOf(
         hasAction(Intent.ACTION_DIAL),
         hasData("tel:" + PHONE_NUMBER),
         toPackage(DIALER_PACKAGE_NAME)));
   }
   // ... code
}

Здесь, hasAction , hasData и toPackage используются вместе с allOf matcher для достижения успеха, только если все сопоставления пройдены.

  • Теперь запустите ExampleInstrumentedTest через меню контента в Android-студии.

Теперь запустите ExampleInstrumentedTest через меню контента в Android-студии.

намереваясь ()

Эспрессо предоставляет специальный метод — намерение (), чтобы высмеивать действие внешнего намерения. intending () принимает имя пакета намерения, которое должно быть смоделировано, и предоставляет метод responseWith, чтобы установить, как смоделированное намерение должно отвечать, как указано ниже,

intending(toPackage("com.android.contacts")).respondWith(result);

Здесь responseWith () принимает намеренный результат типа Instrumentation.ActivityResult . Мы можем создать новое намерение заглушки и вручную установить результат, как указано ниже,

// Stub intent
Intent intent = new Intent();
intent.setData(Uri.parse("content://com.android.contacts/data/1"));
Instrumentation.ActivityResult result =
   new Instrumentation.ActivityResult(Activity.RESULT_OK, intent); 

Полный код для проверки правильности открытия контактного приложения:

@Test
public void stubIntentTest() {
   // Stub intent
   Intent intent = new Intent();
   intent.setData(Uri.parse("content://com.android.contacts/data/1"));
   Instrumentation.ActivityResult result =
      new Instrumentation.ActivityResult(Activity.RESULT_OK, intent);
   intending(toPackage("com.android.contacts")).respondWith(result);
   
   // find the button and perform click action
   onView(withId(R.id.call_contact_button)).perform(click());
   
   // get context
   Context targetContext2 = InstrumentationRegistry.getInstrumentation().getTargetContext();
   
   // get phone number
   String[] projection = { ContactsContract.CommonDataKinds.Phone.NUMBER,
      ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME };
   Cursor cursor =
      targetContext2.getContentResolver().query(Uri.parse("content://com.android.cont
      acts/data/1"), projection, null, null, null);
   
   cursor.moveToFirst();
   int numberColumnIndex =
      cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
   String number = cursor.getString(numberColumnIndex);
   
   // now, check the data
   onView(withId(R.id.edit_text_phone_number))
   .check(matches(withText(number)));
}

Здесь мы создали новое намерение и установили возвращаемое значение (при вызове намерения) в качестве первой записи списка контактов, содержимое: //com.android.contacts/data/1 . Затем мы установили метод intending, чтобы смоделировать вновь созданное намерение вместо списка контактов. Он устанавливает и вызывает наше вновь созданное намерение, когда вызывается пакет com.android.contacts и возвращается первая запись списка по умолчанию. Затем мы запустили действие click (), чтобы запустить фиктивное намерение и, наконец, проверили, совпадают ли телефонный номер из вызывающего фиктивного намерения и номер первой записи в списке контактов.

Если есть проблема с отсутствующим импортом, исправьте эти проблемы с помощью опции Alt + Enter, предоставляемой android studio, или включите приведенные ниже операторы импорта,

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;

import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasData;
import static androidx.test.espresso.intent.matcher.IntentMatchers.toPackage;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.*;

Добавьте приведенное ниже правило в тестовый класс, чтобы предоставить разрешение на чтение списка контактов.

@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(Manifest.permission.READ_CONTACTS);

Добавьте указанную ниже опцию в файл манифеста приложения, AndroidManifest.xml

<uses-permission android:name = "android.permission.READ_CONTACTS" />

Теперь убедитесь, что в списке контактов есть хотя бы одна запись, а затем запустите тест с помощью контекстного меню Android Studio.

Интерфейс для нескольких приложений

Android поддерживает тестирование пользовательского интерфейса, которое включает более одного приложения. Давайте рассмотрим, что у нашего приложения есть возможность перейти от нашего приложения к приложению обмена сообщениями, чтобы отправить сообщение, а затем вернуться к нашему приложению. В этом сценарии среда тестирования пользовательского интерфейса помогает нам тестировать приложение. UI automator можно рассматривать как хороший компаньон для фреймворка эспрессо. Мы можем воспользоваться опцией intending () в среде тестирования эспрессо, прежде чем выбирать автомат для пользовательского интерфейса .

Инструкция по настройке

Android предоставляет UI Automator как отдельный плагин. Это должно быть настроено в app / build.gradle, как указано ниже,

dependencies {
   ...
   androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}

Рабочий процесс для написания тестового примера

Давайте разберемся, как написать тестовый пример на основе UI Automator ,

  • Получить объект UiDevice , вызвав метод getInstance () и передав объект Instrumentation .

Получить объект UiDevice , вызвав метод getInstance () и передав объект Instrumentation .

myDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
myDevice.pressHome();
  • Получить объект UiObject с помощью метода findObject () . Прежде чем использовать этот метод, мы можем открыть приложение uiautomatorviewer для проверки компонентов пользовательского интерфейса целевого приложения, поскольку понимание целевого приложения позволяет нам писать лучшие тестовые случаи.

Получить объект UiObject с помощью метода findObject () . Прежде чем использовать этот метод, мы можем открыть приложение uiautomatorviewer для проверки компонентов пользовательского интерфейса целевого приложения, поскольку понимание целевого приложения позволяет нам писать лучшие тестовые случаи.

UiObject button = myDevice.findObject(new UiSelector()
   .text("Run")
   .className("android.widget.Button"));
  • Смоделируйте взаимодействие с пользователем, вызвав метод UiObject . Например, setText () для редактирования текстового поля и click () для запуска события нажатия кнопки.

Смоделируйте взаимодействие с пользователем, вызвав метод UiObject . Например, setText () для редактирования текстового поля и click () для запуска события нажатия кнопки.

if(button.exists() && button.isEnabled()) {
   button.click();
}
  • Наконец, мы проверяем, отражает ли пользовательский интерфейс ожидаемое состояние.

Наконец, мы проверяем, отражает ли пользовательский интерфейс ожидаемое состояние.

Espresso Testing Framework — Тестовый регистратор

Написание контрольного примера — утомительная работа. Несмотря на то, что espresso предоставляет очень простой и гибкий API, написание тестовых примеров может быть ленивой и трудоемкой задачей. Чтобы преодолеть это, Android Studio предоставляет возможность записывать и генерировать контрольные тесты для эспрессо. Запись эспрессо-теста доступна в меню « Выполнить» .

Давайте запишем простой тестовый пример в нашем HelloWorldApp , выполнив шаги, описанные ниже,

  • Откройте Android-студию, а затем приложение HelloWorldApp .

  • Нажмите RunRecord Espresso test и выберите MainActivity .

  • Скриншот Recorder выглядит следующим образом:

Откройте Android-студию, а затем приложение HelloWorldApp .

Нажмите RunRecord Espresso test и выберите MainActivity .

Скриншот Recorder выглядит следующим образом:

Скриншот рекордера

  • Нажмите Добавить утверждение . Откроется экран приложения, как показано ниже,

Нажмите Добавить утверждение . Откроется экран приложения, как показано ниже,

Экран как показано

  • Нажмите Hello World! , Экран Recorder для выбора вида текста выглядит следующим образом:

Нажмите Hello World! , Экран Recorder для выбора вида текста выглядит следующим образом:

Экран записи

  • Снова нажмите Сохранить утверждение. Это сохранит утверждение и покажет его следующим образом:

Снова нажмите Сохранить утверждение. Это сохранит утверждение и покажет его следующим образом:

Утверждение

  • Нажмите ОК . Это откроет новое окно и спросит название контрольного примера. Имя по умолчанию — MainActivityTest

  • При необходимости измените имя тестового набора.

  • Снова нажмите ОК . Это создаст файл MainActivityTest с нашим записанным тестовым примером. Полное кодирование выглядит следующим образом:

Нажмите ОК . Это откроет новое окно и спросит название контрольного примера. Имя по умолчанию — MainActivityTest

При необходимости измените имя тестового набора.

Снова нажмите ОК . Это создаст файл MainActivityTest с нашим записанным тестовым примером. Полное кодирование выглядит следующим образом:

package com.tutorialspoint.espressosamples.helloworldapp;

import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import androidx.test.espresso.ViewInteraction;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;

@LargeTest
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
   @Rule
   public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
   @Test
   public void mainActivityTest() {
      ViewInteraction textView = onView(
         allOf(withId(R.id.textView_hello), withText("Hello World!"),
         childAtPosition(childAtPosition(withId(android.R.id.content),
         0),0),isDisplayed()));
      textView.check(matches(withText("Hello World!")));
   }
   private static Matcher<View> childAtPosition(
      final Matcher<View> parentMatcher, final int position) {
      return new TypeSafeMatcher<View>() {
         @Override
         public void describeTo(Description description) {
            description.appendText("Child at position " + position + " in parent ");
            parentMatcher.describeTo(description);
         }
         @Override
         public boolean matchesSafely(View view) {
            ViewParent parent = view.getParent();
            return parent instanceof ViewGroup &&
               parentMatcher.matches(parent)&& view.equals(((ViewGroup)
               parent).getChildAt(position));
         }
      };
   }
}
  • Наконец, запустите тест с помощью контекстного меню и проверьте, запускается ли тест.

Наконец, запустите тест с помощью контекстного меню и проверьте, запускается ли тест.

Espresso Testing Framework — производительность пользовательского интерфейса

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

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

dumpsys

dumpsys — это встроенный инструмент, доступный в устройстве Android. Он выводит текущую информацию о системных службах. У dumpsys есть возможность выгрузить информацию о конкретной категории. Передача gfxinfo предоставит анимационную информацию о поставляемом пакете. Команда выглядит следующим образом:

> adb shell dumpsys gfxinfo <PACKAGE_NAME>

framestats

framestats — это опция команды dumpsys. Как только dumpsys вызывается с framestats , он выводит подробную информацию о синхронизации кадров последних кадров. Команда выглядит следующим образом:

> adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats

Он выводит информацию в виде CSV (значения, разделенные запятыми). Вывод в формате CSV помогает легко перенести данные в Excel и впоследствии извлечь полезную информацию с помощью формул и диаграмм Excel.

Systrace

Systrace также встроенный инструмент, доступный в устройстве Android. Он фиксирует и отображает время выполнения процессов приложения. Systrace может быть запущен с помощью команды ниже в терминале андроид студии,

python %ANDROID_HOME%/platform-tools/systrace/systrace.py --time=10 -o
my_trace_output.html gfx view res

Espresso Testing Framework — Доступность

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

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

В Espresso разработчик может включить и настроить тестирование специальных возможностей с помощью класса AccessibilityChecks . Пример кода выглядит следующим образом:

AccessibilityChecks.enable();

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

AccessibilityChecks.enable().setRunChecksFromRootView(true);

Заключение

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