Przykład testów z samourządzeniem

Gdy rozpoczyna się test instrumentacji, uruchamia się docelowy pakiet z wstrzykniętym kodem instrumentacji i inicjuje jego wykonanie. Jedynym wyjątkiem jest to, że docelowy pakiet nie może być sam framework aplikacji na Androida, np. pakiet android, ponieważ prowadzi to do paradoksalnej sytuacji, w której trzeba by ponownie uruchomić framework Androida, który obsługuje funkcje systemu, w tym samą instrumentację.

Oznacza to, że test z instrumentacją nie może się wstrzyknąć do frameworku Androida (czyli serwera systemowego) w celu wykonania. Aby przetestować interfejs Androida, kod testowy może wywoływać tylko publiczne interfejsy API lub te udostępnione 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 konkretny pakiet. Dlatego zwykle takie instrumentacje są deklarowane jako kierowane na własny pakiet aplikacji testowej, jak określono w tagu <manifest> w elementach AndroidManifest.xml.

W zależności od wymagań pakiety testów aplikacji z tej kategorii mogą też:

  • Aktywności pakietu potrzebne do testowania.
  • Udostępnij systemowi identyfikator użytkownika.
  • być podpisane kluczem platformy;
  • być skompilowany na podstawie kodu źródłowego frameworka, a nie publicznego pakietu SDK;

Ta kategoria testów jest czasami nazywana autoinstrumentacją. Oto kilka przykładów testów samourządzania w źródle platformy:

W tym przykładzie omawiamy tworzenie nowego testu pomiarowego z docelowym pakietem ustawionym na własny pakiet aplikacji testowej. W tym przewodniku jako przykład użyto tego testu:

Zalecamy, aby przed dalszą pracą przejrzeć kod.

Wybierz lokalizację źródłową

Zespół ma zwykle ustalony schemat miejsc, w których należy sprawdzić kod, oraz miejsc, w których należy dodać testy. Większość zespołów ma jedno repozytorium Git lub udostępnia je innym zespołom, ale ma też specjalny podkatalog z kodem źródłowym komponentu.

Zakładając, że katalog główny źródła komponentu znajduje się w folderze <component source root>, większość komponentów ma pod sobą foldery srctests oraz niektóre dodatkowe pliki, takie jak Android.mk (lub podzielone na dodatkowe pliki .mk), plik manifestu AndroidManifest.xml oraz plik konfiguracji testu „AndroidTest.xml”.

Ponieważ dodajesz zupełnie nowy test, prawdopodobnie musisz utworzyć katalog tests obok komponentu src i wypełnić go treścią.

W niektórych przypadkach Twój zespół może mieć dodatkowe struktury katalogów w folderze testsze względu na konieczność spakowania różnych zestawów testów do poszczególnych plików APK. W tym przypadku musisz utworzyć nowy katalog podrzędny w folderze tests.

Niezależnie od struktury katalogu tests lub nowo utworzonego podkatalogu wypełnisz plikami podobnymi do tych, które znajdują się w katalogu instrumentation w przykładowej zmianie w 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 instrumentacji wymaga pliku manifestu o nazwie AndroidManifest.xml. Aby automatycznie uwzględnić ten plik za pomocą pliku make BUILD_PACKAGE, podaj go obok pliku Android.mk dla modułu testowego.

Jeśli nie znasz pliku AndroidManifest.xml, zapoznaj się z artykułem Omówienie pliku 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>

Wybrane uwagi dotyczące 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 używa platforma aplikacji na Androida do identyfikowania aplikacji (w tym kontekście: Twojej aplikacji testowej). Każdy użytkownik w systemie może zainstalować tylko jedną aplikację o tej nazwie pakietu.

Co więcej, ten atrybut package jest taki sam jak ten zwracany przez ComponentName#getPackageName(), a także ten, którego używasz do interakcji z różnymi podkomendami pm, które używają adb shell.

Pamiętaj, że chociaż nazwa pakietu jest zwykle w tym samym stylu co nazwa pakietu Java, w istocie ma z nią niewiele wspólnego. Innymi słowy, pakiet aplikacji (lub testu) może zawierać klasy o dowolnych nazwach pakietów, ale z drugiej strony możesz postawić na prostotę i użyć w aplikacji lub teście nazwy pakietu Java na najwyższym poziomie identycznej z nazwą pakietu aplikacji.

android:sharedUserId="android.uid.system"

Oznacza to, że w momencie instalacji plik APK powinien mieć ten sam identyfikator użytkownika, czyli tożsamość w czasie wykonywania, co platforma główna. Pamiętaj, że zależy to od tego, czy plik APK jest podpisany tym samym certyfikatem co platforma główna (patrz LOCAL_CERTIFICATE w poprzedniej sekcji), ale są to różne koncepcje:

  • 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 system, co wymaga, aby pakiet wywołujący udostępniał identyfikator użytkownika usłudze system, jeśli jest to pakiet oddzielony 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ą pakowane w oddzielnym pliku biblioteki JAR platformy, co wymaga dodatkowych wpisów w ścieżce klasyfikacji, gdy pakiet testów jest wywoływany przez platformę aplikacji.

android:targetPackage="android.test.example.helloworld"

Zauważysz pewnie, że atrybut targetPackage jest tu zadeklarowany tak samo jak atrybut package w tagu manifest w tym pliku. Jak wspomniano w podstawach testowania, ta kategoria testów jest zwykle przeznaczona do testowania interfejsów API frameworków, więc nie ma większego znaczenia, czy testy są kierowane na konkretny pakiet aplikacji.

Prosty plik konfiguracji

Każdy nowy moduł testowy musi mieć plik konfiguracji, który kieruje system kompilacji za pomocą metadanych modułu, zależności w czasie kompilacji i instrukcji pakowania. W większości przypadków wystarczająca jest opcja pliku Blueprint na podstawie Soong. Szczegółowe informacje znajdziesz w sekcji Prosta konfiguracja testu.

złożony plik konfiguracji,

W takich bardziej złożonych przypadkach musisz też napisać plik konfiguracji testów dla testowego zestawu narzędzi Androida, Trade Federation.

Konfiguracja testu może określać specjalne opcje konfiguracji urządzenia i domyślne argumenty do przekazania do klasy testu. Przykład znajdziesz w pliku /platform_testing/tests/example/instrumentation/AndroidTest.xml.

Dla wygody użytkownika zamieszczamy 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>

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

Ta instrukcja mówi, aby usługa Trade Federation zainstalowała plik HelloWorldTests.apk na urządzeniu docelowym za pomocą określonego narzędzia target_preparer. W ramach Trade Federation deweloperzy mają do dyspozycji wiele narzędzi do przygotowywania urządzeń, które można wykorzystać do sprawdzenia, czy urządzenie jest prawidłowo skonfigurowane przed wykonaniem testu.

<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 klasę testu Trade Federation, której należy użyć do wykonania testu, oraz przekazuje pakiet na urządzenie, na którym ma być wykonany, oraz testowy framework, którym w tym przypadku jest JUnit.

Więcej informacji znajdziesz w artykule o konfiguracjach testowych modułów.

Funkcje JUnit4

Korzystanie z biblioteki android-support-test jako test runnera umożliwia stosowanie nowych klas testów w stylu JUnit4, a przykładowa zmiana w 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 wzorce, które są przydatne na ogół.

@RunWith(JUnit4.class)
public class HelloWorldTest {

Istotną różnicą w JUnit4 jest to, że testy nie muszą już dziedziczyć z ogólnej klasy testów podstawowych. Zamiast tego piszesz testy w zwykłych klasach Java i używasz adnotacji, aby wskazać określone ustawienia testu i ograniczenia. W tym przykładzie wskazujemy, ż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@After są używane w metodach przez JUnit4 do konfiguracji przed testem i rozmontowania po teście. Podobnie adnotacje @BeforeClass@AfterClass są używane w metodach przez JUnit4 do konfiguracji przed wykonaniem wszystkich testów w klasie testów, a potem do jej demontażu. Pamiętaj, że metody konfiguracji i rozwiązywania na poziomie klasy muszą być statyczne. W przypadku metod testów w przeciwieństwie do wcześniejszej wersji JUnit nazwy metod nie muszą już zaczynać się od test. Każda z nich musi być oznaczona znacznikiem @Test. Jak zwykle metody testów muszą być publiczne, nie mogą deklarować wartości zwracanej, nie mogą przyjmować parametrów i mogą zgłaszać wyjątki.

Dostęp do klasy pomiarowej

Chociaż nie jest to omówione w przykładzie Hello World, testy na Androidzie często wymagają dostępu do instancji Instrumentation: to podstawowy interfejs API, który zapewnia dostęp do kontekstów aplikacji, interfejsów API testów związanych z cyklem życia aktywności i innych.

Testy JUnit4 nie wymagają już wspólnej klasy bazowej, więc nie trzeba uzyskiwać instancji Instrumentation za pomocą InstrumentationTestCase#getInstrumentation(). Nowy testujący zarządza nią za pomocą InstrumentationRegistry, gdzie przechowywany jest kontekst i ustawienia środowiska utworzone przez framework pomiarowy.

Aby uzyskać dostęp do instancji klasy Instrumentation, po prostu wywołaj stałą metodę getInstrumentation() w klasie InstrumentationRegistry:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

Kompilowanie i testowanie lokalnie

W przypadku najczęstszych zastosowań użyj poświadczenia.

W bardziej złożonych przypadkach wymagających bardziej zaawansowanych dostosowań postępuj zgodnie z instrukcjami dotyczącymi implementacji.