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

Когда запускается тест инструментации, его целевой пакет перезапускается с внедренным кодом инструментации и инициируется для выполнения. Единственным исключением является то, что целевой пакет здесь не может быть самим фреймворком приложения 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 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. Подробности см. в разделе Simple Test Configuration .

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

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

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

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