Po uruchomieniu testu z instrumentacją jego pakiet docelowy jest ponownie uruchamiany z wstrzykniętym kodem instrumentacji i inicjowany do wykonania. Wyjątkiem jest to, że pakiet docelowy nie może być samym frameworkiem aplikacji na Androida, np. pakietem android, ponieważ prowadzi to do paradoksalnej sytuacji, w której trzeba by ponownie uruchomić frameworka Androida, który obsługuje funkcje systemowe, w tym samą instrumentację.
Oznacza to, że test instrumentacji nie może wstrzyknąć się do frameworka Androida, czyli serwera systemowego, w celu wykonania. Aby przetestować frameworka Androida, kod testowy może wywoływać tylko publiczne interfejsy API lub te, które są udostępniane
za pomocą języka definiowania interfejsu Androida
AIDL
dostępnego w drzewie źródłowym platformy. W przypadku tej kategorii testów nie ma sensu kierowanie na żaden konkretny pakiet. Dlatego zwykle takie
instrumentacje są deklarowane jako kierowane na własny pakiet aplikacji testowej, zgodnie z
definicją w tagu <manifest> w pliku AndroidManifest.xml.
W zależności od wymagań pakiety aplikacji testowych w tej kategorii mogą też:
- zawierać działania potrzebne do testowania;
- udostępniać identyfikator użytkownika systemowi;
- być podpisane kluczem platformy;
- być kompilowane na podstawie kodu źródłowego frameworka, a nie publicznego pakietu SDK.
Ta kategoria testów instrumentacji jest czasami nazywana instrumentacją własną. Oto kilka przykładów testów instrumentacji własnej w kodzie źródłowym platformy:
Przykładem, który tu omawiamy, jest napisanie nowego testu z instrumentacją z pakietem docelowym ustawionym na własny pakiet aplikacji testowej. W tym przewodniku jako przykładu używamy tego testu:
Zanim przejdziesz dalej, zalecamy najpierw zapoznać się z kodem, aby uzyskać ogólne pojęcie o jego działaniu.
Wybierz lokalizację źródła
Zwykle Twój zespół ma już ustalone miejsca, w których należy sprawdzać kod, oraz miejsca, w których należy dodawać testy. Większość zespołów ma jedno repozytorium Git lub udostępnia je innym zespołom, ale ma dedykowany podkatalog zawierający kod źródłowy komponentu.
Zakładając, że lokalizacja główna kodu źródłowego komponentu to <component source
root>, większość komponentów ma w niej foldery src i tests oraz kilka
dodatkowych plików, takich jak Android.mk (lub podzielony na dodatkowe pliki .mk),
plik manifestu AndroidManifest.xml i plik konfiguracji testu
'AndroidTest.xml'.
Ponieważ dodajesz zupełnie nowy test, prawdopodobnie musisz utworzyć katalog tests obok katalogu src komponentu i wypełnić go treścią.
W niektórych przypadkach Twój zespół może mieć dodatkowe struktury katalogów w folderze tests ze względu na konieczność spakowania różnych zestawów testów w osobne pliki APK. W takim przypadku musisz utworzyć nowy podkatalog w folderze tests.
Niezależnie od struktury wypełnisz katalog tests lub nowo utworzony podkatalog plikami podobnymi do tych, które znajdują się w katalogu instrumentation w przykładowej zmianie Gerrit. Szczegóły dotyczące każdego pliku znajdziesz w dalszej części tego dokumentu.
Plik manifestu
Podobnie jak w przypadku projektu aplikacji, każdy moduł testu z instrumentacją wymaga pliku manifestu o nazwie AndroidManifest.xml. Aby automatycznie uwzględnić ten plik za pomocą podstawowego pliku makefile BUILD_PACKAGE, umieść go obok pliku Android.mk modułu testowego.
Jeśli nie znasz pliku AndroidManifest.xml, zapoznaj się z
Przeglądem manifestu aplikacji
Oto przykładowy plik 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>
Kilka uwag na temat pliku manifestu:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.test.example.helloworld" >
Atrybut package to nazwa pakietu aplikacji. Jest to unikalny identyfikator, którego framework aplikacji na Androida używa do identyfikowania aplikacji (lub w tym kontekście: aplikacji testowej). Każdy użytkownik w systemie może zainstalować tylko 1 aplikację o tej nazwie pakietu.
Ponadto ten atrybut package jest taki sam jak wartość zwracana przez
ComponentName#getPackageName()
oraz taki sam jak ten, którego używasz do interakcji z różnymi poleceniami podrzędnymi pm za pomocą adb shell.
Pamiętaj, że chociaż nazwa pakietu jest zwykle w tym samym stylu co nazwa pakietu Java, ma z nią niewiele wspólnego. Innymi słowy, pakiet aplikacji (lub testu) może zawierać klasy o dowolnych nazwach pakietów. Z drugiej strony możesz wybrać prostotę i użyć nazwy pakietu Java najwyższego poziomu w aplikacji lub teście, która będzie identyczna z nazwą pakietu aplikacji.
android:sharedUserId="android.uid.system"
Deklaruje to, że podczas instalacji ten plik APK powinien mieć przyznany ten sam identyfikator użytkownika, czyli tożsamość środowiska wykonawczego, co platforma podstawowa. Pamiętaj, że zależy to od tego, czy plik APK jest podpisany tym samym certyfikatem co platforma podstawowa (patrz LOCAL_CERTIFICATE w poprzedniej sekcji), ale są to różne pojęcia:
- niektóre uprawnienia lub interfejsy API są chronione podpisem, co wymaga użycia tego samego certyfikatu podpisywania;
- niektóre uprawnienia lub interfejsy API wymagają tożsamości użytkownika
systemwywołującego, co wymaga, aby pakiet wywołujący udostępniał identyfikator użytkownika pakietowisystem, jeśli jest to oddzielny pakiet od samej platformy podstawowej.
<uses-library android:name="android.test.runner" />
Jest to wymagane w przypadku wszystkich testów instrumentacji, ponieważ powiązane klasy są spakowane w osobnym pliku JAR frameworka, dlatego gdy pakiet testowy jest wywoływany przez framework aplikacji, wymaga dodatkowych wpisów w ścieżce klasy.
android:targetPackage="android.test.example.helloworld"
Możesz zauważyć, że targetPackage jest tu zadeklarowany tak samo jak atrybut package zadeklarowany w tagu manifest tego pliku. Jak wspomnieliśmy w
podstawach testowania, ta kategoria testów z instrumentacją jest
zwykle przeznaczona do testowania interfejsów API platformy, więc nie ma sensu, aby
miały one konkretny pakiet aplikacji docelowej inny niż własny.
Prosty plik konfiguracji
Każdy nowy moduł testowy musi mieć plik konfiguracji, który kieruje systemem kompilacji za pomocą metadanych modułu, zależności czasu kompilacji i instrukcji pakowania. W większości przypadków wystarczy opcja pliku Blueprint opartego na Soong. Szczegółowe informacje znajdziesz w artykule Prosta konfiguracja testu.
Złożony plik konfiguracji
Konfiguracja testu może określać specjalne opcje konfiguracji urządzenia i argumenty domyślne, które mają być przekazywane do klasy testowej. Przykład znajdziesz w pliku /platform_testing/tests/example/instrumentation/AndroidTest.xml.
Dla wygody dołączamy tu zrzut ekranu:
<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>
Kilka uwag na temat pliku konfiguracji testu:
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>
Informuje to Trade Federation, aby zainstalował plik HelloWorldTests.apk na urządzeniu docelowym za pomocą określonego target_preparer. W Trade Federation deweloperzy mają do dyspozycji wiele preparerów docelowych, których można użyć, aby przed wykonaniem testu upewnić się, że urządzenie jest prawidłowo skonfigurowane.
<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>
Określa to klasę testową Trade Federation, która ma być używana do wykonania testu, i przekazuje pakiet na urządzeniu do wykonania oraz framework testowy, którym w tym przypadku jest JUnit.
Więcej informacji znajdziesz w artykule Konfiguracje modułów testowych.
Funkcje JUnit4
Użycie biblioteki android-support-test jako narzędzia do testowania umożliwia przyjęcie nowych klas testowych w stylu JUnit4, a przykładowa zmiana Gerrit zawiera bardzo podstawowe użycie jej funkcji. Przykład znajdziesz w pliku
/platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java.
Chociaż wzorce testowania są zwykle specyficzne dla zespołów komponentów, istnieją pewne ogólnie przydatne wzorce użycia.
@RunWith(JUnit4.class)
public class HelloWorldTest {
Istotną różnicą w JUnit4 jest to, że testy nie muszą już dziedziczyć po wspólnej podstawowej klasie testowej. Zamiast tego piszesz testy w zwykłych klasach Java i używasz adnotacji, aby wskazać określone ustawienia i ograniczenia testu. W tym przykładzie informujemy, że ta klasa powinna być uruchamiana jako test JUnit4.
@BeforeClass
public static void beforeClass() {
...
@AfterClass
public static void afterClass() {
...
@Before
public void before() {
...
@After
public void after() {
...
@Test
@SmallTest
public void testHelloWorld() {
...
Adnotacje @Before i @After są używane w metodach przez JUnit4 do wykonywania konfiguracji przed testem i czyszczenia po teście. Podobnie adnotacje @BeforeClass i @AfterClass są używane w metodach przez JUnit4 do wykonywania konfiguracji przed wykonaniem wszystkich testów w klasie testowej i czyszczenia po nich. Pamiętaj, że metody konfiguracji i czyszczenia w zakresie klasy muszą być statyczne. Jeśli chodzi o metody testowe, w przeciwieństwie do wcześniejszych wersji JUnit nie muszą one już zaczynać nazwy metody od test. Zamiast tego każda z nich musi być oznaczona adnotacją @Test. Jak zwykle metody testowe muszą być publiczne, nie mogą deklarować wartości zwracanej, nie mogą przyjmować parametrów i mogą zgłaszać wyjątki.
Dostęp do klasy instrumentacji
Chociaż nie jest to omówione w podstawowym przykładzie Hello World, dość często test na Androida wymaga dostępu do instancji Instrumentation. Jest to podstawowy interfejs API, który zapewnia dostęp do kontekstów aplikacji, interfejsów API testów związanych z cyklem życia działania i innych.
Ponieważ testy JUnit4 nie wymagają już wspólnej klasy podstawowej, nie trzeba już
uzyskiwać instancji Instrumentation za pomocą
InstrumentationTestCase#getInstrumentation(). Zamiast tego nowy narzędzie do testowania
zarządza nią za pomocą InstrumentationRegistry
, w którym są
przechowywane kontekstowe i środowiskowe ustawienia utworzone przez framework instrumentacji.
Aby uzyskać dostęp do instancji klasy Instrumentation, wystarczy wywołać metodę statyczną
getInstrumentation() w klasie InstrumentationRegistry:
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()
Kompilowanie i testowanie lokalne
W najczęstszych przypadkach użycia stosuj Atest.
W bardziej złożonych przypadkach wymagających większej personalizacji postępuj zgodnie z instrukcjami dotyczącymi instrumentacji.