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