Пример самоинструментационных тестов

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

Это означает, что инструментальный тест не может внедряться в фреймворк Android, то есть в системный сервер, для выполнения. Для тестирования фреймворка Android тестовый код может вызывать только общедоступные API-интерфейсы или те, которые предоставляются с помощью языка определения интерфейса Android (AIDL), доступного в исходном коде платформы. Для этой категории тестов нет смысла ориентироваться на какой-либо конкретный пакет. Поэтому обычно такие инструментальные тесты объявляются как ориентированные на собственный пакет тестового приложения, как определено в собственном теге <manifest> файла AndroidManifest.xml .

В зависимости от требований, пакеты тестовых приложений этой категории могут также:

  • Объедините действия, необходимые для тестирования.
  • Передайте системе идентификатор пользователя.
  • Для входа используйте ключ платформы.
  • Компиляция должна выполняться с использованием исходного кода фреймворка, а не общедоступного SDK.

Этот тип инструментальных тестов иногда называют самоинструментализацией. Вот несколько примеров самоинструментативных тестов в исходном коде платформы:

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

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

Определите местоположение источника.

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

Предположим, что корневая директория вашего компонента находится по адресу <component source root> . В большинстве компонентов под ней находятся папки src и tests , а также некоторые дополнительные файлы, такие как Android.mk (или разбитые на дополнительные файлы .mk ), файл манифеста AndroidManifest.xml и файл конфигурации тестов 'AndroidTest.xml'.

Поскольку вы добавляете совершенно новый тест, вам, вероятно, потребуется создать каталог tests рядом с src вашего компонента и заполнить его содержимым.

В некоторых случаях у вашей команды может быть более сложная структура каталогов в разделе tests из-за необходимости упаковывать различные наборы тестов в отдельные APK-файлы. В этом случае вам потребуется создать новый подкаталог в папке tests .

Независимо от структуры, в итоге вы заполните каталог tests или вновь созданный подкаталог файлами, аналогичными тем, что находятся в каталоге instrumentation в примере изменений Gerrit. Подробное описание каждого файла приведено далее в этом документе.

файл манифеста

Как и в случае с проектом приложения, каждому модулю инструментального тестирования требуется файл манифеста под названием AndroidManifest.xml . Чтобы автоматически включить этот файл с помощью основного makefile BUILD_PACKAGE , укажите этот файл рядом с файлом Android.mk для вашего тестового модуля.

Если вам незнаком файл AndroidManifest.xml , обратитесь к обзору App Manifest.

Ниже приведён пример файла AndroidManifest.xml :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  android:sharedUserId="android.uid.system"
  package="android.test.example.helloworld" >

    <application>
       <uses-library android:name="android.test.runner"/>
    </application>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

Несколько замечаний по поводу файла манифеста:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

Атрибут package — это имя пакета приложения: это уникальный идентификатор, который платформа Android использует для идентификации приложения (или в данном контексте: вашего тестового приложения). Каждый пользователь в системе может установить только одно приложение с таким именем пакета.

Кроме того, этот атрибут package идентичен тому, что возвращает ComponentName#getPackageName() , а также тому, который вы использовали бы для взаимодействия с различными подкомандами pm с помощью adb shell .

Обратите внимание, что хотя имя пакета обычно имеет тот же стиль, что и имя пакета Java, на самом деле оно имеет с ним очень мало общего. Другими словами, ваш пакет приложения (или теста) может содержать классы с любыми именами пакетов, хотя, с другой стороны, вы можете выбрать простоту и сделать имя пакета Java верхнего уровня в вашем приложении или тесте идентичным имени пакета приложения.

android:sharedUserId="android.uid.system"

Это означает, что во время установки этому APK-файлу должен быть присвоен тот же идентификатор пользователя, то есть идентификатор среды выполнения, что и основной платформе. Обратите внимание, что это зависит от того, подписан ли APK-файл тем же сертификатом, что и основная платформа (см. LOCAL_CERTIFICATE в предыдущем разделе), однако это разные понятия:

  • Некоторые разрешения или API защищены подписью, что требует использования одного и того же сертификата подписи.
  • Для некоторых разрешений или API требуется system идентификатор пользователя вызывающей стороны, что требует от вызывающего пакета предоставления идентификатора пользователя system , если это отдельный пакет от основной платформы.
<uses-library android:name="android.test.runner" />

Это необходимо для всех инструментальных тестов, поскольку соответствующие классы упакованы в отдельный JAR-файл библиотеки фреймворка, поэтому при вызове тестового пакета фреймворком требуются дополнительные записи в classpath.

android:targetPackage="android.test.example.helloworld"

Возможно, вы заметили, что атрибут targetPackage здесь объявлен так же, как и атрибут package указанный в теге manifest этого файла. Как упоминалось в разделе «Основы тестирования» , эта категория инструментальных тестов обычно предназначена для тестирования API фреймворков, поэтому для них не очень целесообразно иметь конкретный целевой пакет приложения, кроме самого себя.

Простой конфигурационный файл

Для каждого нового тестового модуля необходим конфигурационный файл, который будет направлять систему сборки, передавая метаданные модуля, зависимости времени компиляции и инструкции по упаковке. В большинстве случаев достаточно использовать вариант с файлом Blueprint на основе Soong. Подробнее см. раздел «Простая конфигурация тестов» .

Сложный конфигурационный файл

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

В конфигурации теста можно указать специальные параметры настройки устройства и аргументы по умолчанию для предоставления тестового класса. См. пример по адресу /platform_testing/tests/example/instrumentation/AndroidTest.xml .

Для удобства здесь приведена сводка:

<configuration description="Runs sample instrumentation test.">
  <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
  <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
    <option name="test-file-name" value="HelloWorldTests.apk"/>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
  <option name="test-suite-tag" value="apct"/>
  <option name="test-tag" value="SampleInstrumentationTest"/>

  <test class="com.android.tradefed.testtype.AndroidJUnitTest">
    <option name="package" value="android.test.example.helloworld"/>
    <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
  </test>
</configuration>

Несколько замечаний по поводу файла конфигурации теста:

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>

Это указывает Trade Federation установить файл HelloWorldTests.apk на целевое устройство, используя указанный target_preparer. В Trade Federation разработчикам доступно множество target_preparer, которые можно использовать для обеспечения правильной настройки устройства перед выполнением тестов.

<test class="com.android.tradefed.testtype.AndroidJUnitTest">
  <option name="package" value="android.test.example.helloworld"/>
  <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>

Это указывает на используемый для выполнения теста тестовый класс Trade Federation, передает параметры пакета на устройстве, которое необходимо запустить, и на среду выполнения тестов, которой в данном случае является JUnit.

Для получения более подробной информации см. раздел «Конфигурации тестового модуля» .

Функции JUnit4

Использование библиотеки android-support-test в качестве средства запуска тестов позволяет внедрять новые тестовые классы в стиле JUnit4, а пример изменения в Gerrit содержит некоторые очень простые примеры использования её возможностей. См. пример по адресу /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java .

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

@RunWith(JUnit4.class)
public class HelloWorldTest {

Существенное отличие JUnit4 заключается в том, что тестам больше не требуется наследовать от общего базового тестового класса; вместо этого вы пишете тесты в обычных Java-классах и используете аннотации для указания определенных параметров и ограничений теста. В этом примере мы указываем, что этот класс должен быть запущен как тест JUnit4.

    @BeforeClass
    public static void beforeClass() {
    ...
    @AfterClass
    public static void afterClass() {
    ...
    @Before
    public void before() {
    ...
    @After
    public void after() {
    ...
    @Test
    @SmallTest
    public void testHelloWorld() {
    ...

Аннотации @Before и ` @After используются в методах JUnit4 для выполнения предварительной настройки теста и завершения теста после его выполнения. Аналогично, аннотации @BeforeClass и @AfterClass используются в методах JUnit4 для выполнения настройки перед выполнением всех тестов в тестовом классе и завершения теста после этого. Обратите внимание, что методы настройки и завершения теста в рамках класса должны быть статическими. Что касается тестовых методов, в отличие от более ранних версий JUnit, им больше не нужно начинать имя метода с test , вместо этого каждый из них должен быть аннотирован @Test . Как обычно, тестовые методы должны быть публичными, не объявлять возвращаемое значение, не принимать параметры и могут генерировать исключения.

Доступ к классу измерительных приборов

Хотя это не рассматривается в базовом примере "Hello World", довольно часто для Android-тестов требуется доступ к экземпляру Instrumentation : это основной API-интерфейс, предоставляющий доступ к контекстам приложения, API для тестирования, связанным с жизненным циклом активности, и многому другому.

Поскольку тесты JUnit4 больше не требуют общего базового класса, больше нет необходимости получать экземпляр Instrumentation через InstrumentationTestCase#getInstrumentation() ; вместо этого новый средство запуска тестов управляет им через InstrumentationRegistry , где хранятся контекстные и средовые настройки, созданные фреймворком Instrumentation.

Для доступа к экземпляру класса Instrumentation достаточно вызвать статический метод getInstrumentation() класса InstrumentationRegistry :

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

Сборка и тестирование локально.

Для наиболее распространенных сценариев использования применяйте Atest .

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