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