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

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

Это означает, что инструментальный тест не может быть внедрен во фреймворк Android, то есть в системный сервер, для выполнения. Для тестирования фреймворка Android тестовый код может вызывать только общедоступные API-интерфейсы или 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 . Чтобы автоматически включить этот файл с помощью основного make-файла 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 sub с помощью 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 доступно множество целевых препараторов, которые можно использовать для правильной настройки устройства перед выполнением теста.

<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 .

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