Przykład testów samoprzyrządowania

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Po uruchomieniu testu instrumentacji jego pakiet docelowy jest ponownie uruchamiany z wstrzykniętym kodem instrumentacji i inicjowanym do wykonania. Jedynym wyjątkiem jest to, że pakietem docelowym nie może być sam framework aplikacji na Androida, tj. pakiet android , ponieważ doprowadziłoby to do paradoksalnej sytuacji, w której musiałby zostać zrestartowany framework Androida, który obsługuje funkcje systemowe, w tym oprzyrządowanie samo.

Oznacza to, że test oprzyrządowania nie może wstrzyknąć się do środowiska Androida, czyli serwera systemowego, w celu wykonania. Aby przetestować platformę Androida, kod testowy może wywoływać tylko publiczne powierzchnie API lub udostępniane za pośrednictwem AIDL języka definicji interfejsu Androida dostępnego w drzewie źródłowym platformy. W przypadku tej kategorii testów celowanie w konkretny pakiet nie ma sensu. W związku z tym zwykle deklaruje się, że takie instrumentacje są przeznaczone dla własnego pakietu aplikacji testowej, zgodnie z definicją we własnym <manifest> tagu AndroidManifest.xml .

W zależności od wymagań pakiety aplikacji testowych z tej kategorii mogą również:

  • Połącz działania potrzebne do testowania.
  • Udostępnij identyfikator użytkownika systemowi.
  • Być podpisane kluczem platformy.
  • Należy skompilować ze źródłem platformy, a nie z publicznym zestawem SDK.

Ta kategoria testów oprzyrządowania jest czasami określana jako samooprzyrządowanie. Oto kilka przykładów testów samooprzyrządowania w źródle platformy:

Omówiony tutaj przykład polega na napisaniu nowego testu oprzyrządowania z pakietem docelowym ustawionym we własnym pakiecie aplikacji testowej. W tym przewodniku wykorzystano następujący test jako przykład:

Zaleca się, aby najpierw przejrzeć kod, aby uzyskać ogólne wrażenie przed kontynuowaniem.

Decydowanie o lokalizacji źródła

Zazwyczaj Twój zespół będzie miał już ustalony wzorzec miejsc do zaewidencjonowania kodu i miejsc do dodania testów. Większość zespołów posiada pojedyncze repozytorium git lub udostępnia je innym zespołom, ale ma dedykowany podkatalog zawierający kod źródłowy komponentów.

Zakładając, że lokalizacja główna źródła komponentu to <component source root> , większość komponentów ma foldery src i .mk oraz niektóre dodatkowe pliki, takie jak Android.mk (lub podzielone na dodatkowe pliki tests ), plik manifestu AndroidManifest.xml i testowy plik konfiguracyjny „AndroidTest.xml”.

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

W niektórych przypadkach Twój zespół może testować dalsze struktury katalogów ze względu na potrzebę umieszczania różnych zestawów tests w poszczególnych apkach. W takim przypadku będziesz musiał utworzyć nowy podkatalog w obszarze tests .

Niezależnie od struktury skończysz wypełniając katalog tests lub nowo utworzony podkatalog plikami podobnymi do tego, co znajduje się w katalogu instrumentation w przykładowej zmianie gerrit. Poniższe sekcje wyjaśnią bardziej szczegółowo każdy plik.

Plik manifestu

Podobnie jak w przypadku zwykłej aplikacji, każdy moduł testowy oprzyrządowania wymaga pliku manifestu. Jeśli nazwiesz plik jako AndroidManifest.xml i podasz go obok Android.mk dla swojego modułu testowego, zostanie on automatycznie dołączony do podstawowego makefile BUILD_PACKAGE .

Zanim przejdziesz dalej, zdecydowanie zalecamy przejrzenie omówienia manifestu aplikacji .

Daje to przegląd podstawowych składników pliku manifestu i ich funkcjonalności. Zobacz przykład pod adresem platform_testing/tests/example/instrumentation/AndroidManifest.xml .

Migawka jest tutaj dla wygody:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

    <application/>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

Niektóre 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 używany przez platformę aplikacji Android do identyfikowania aplikacji (lub w tym kontekście: 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, który zwraca ComponentName#getPackageName() , a także ten sam, którego użyłbyś do interakcji z różnymi podkomendami pm przez adb shell .

Należy również zauważyć, że chociaż nazwa pakietu jest zazwyczaj w tym samym stylu, co nazwa pakietu Java, w rzeczywistości ma z nią bardzo niewiele wspólnego. Innymi słowy, twój pakiet aplikacji (lub testu) może zawierać klasy o dowolnych nazwach pakietów, chociaż z drugiej strony możesz zdecydować się na prostotę i mieć nazwę pakietu Java najwyższego poziomu w swojej aplikacji lub teście identyczną z nazwą pakietu aplikacji.

android:sharedUserId="android.uid.system"

Oznacza to, że w czasie instalacji ta aplikacja powinna otrzymać ten sam identyfikator użytkownika, tj. tożsamość środowiska uruchomieniowego, co platforma podstawowa. Zauważ, że zależy to od podpisania apk tym samym certyfikatem co platforma podstawowa (patrz LOCAL_CERTIFICATE w powyższej sekcji), ale są to różne koncepcje:

  • niektóre uprawnienia lub interfejsy API są chronione podpisami, co wymaga tego samego certyfikatu podpisu
  • niektóre uprawnienia lub interfejsy API wymagają tożsamości użytkownika system wywołującego, co wymaga, aby pakiet wywołujący udostępnił identyfikator użytkownika system , 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 oprzyrządowania, ponieważ powiązane klasy są spakowane w osobnym pliku biblioteki jar platformy, dlatego wymagane są dodatkowe wpisy ścieżki klasy, gdy pakiet testowy jest wywoływany przez platformę aplikacji.

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

Być może zauważyłeś, że targetPackage jest tutaj zadeklarowany tak samo, jak atrybut package zadeklarowany w tagu manifest tego pliku. Jak wspomniano w temacie Podstawy testowania , ta kategoria testów oprzyrządowania jest zwykle przeznaczona do testowania interfejsów API platformy, więc nie ma dla nich większego znaczenia posiadanie określonego docelowego pakietu aplikacji, innego niż on sam.

Prosty plik konfiguracyjny

Każdy nowy moduł testowy musi mieć plik konfiguracyjny do kierowania systemem kompilacji z metadanymi modułu, zależnościami w czasie kompilacji i instrukcjami dotyczącymi pakowania. W większości przypadków opcja pliku Blueprint oparta na Soong jest wystarczająca. Aby uzyskać szczegółowe informacje, zobacz Prosta konfiguracja testu .

Złożony plik konfiguracyjny

W tych bardziej złożonych przypadkach należy również napisać testowy plik konfiguracyjny dla wiązki testowej systemu Android, Trade Federation .

Konfiguracja testu może określać specjalne opcje konfiguracji urządzenia i domyślne argumenty w celu dostarczenia klasy testowej. Zobacz przykład w /platform_testing/tests/example/instrumentation/AndroidTest.xml .

Migawka jest tutaj dla wygody:

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

Niektóre wybrane uwagi dotyczące pliku konfiguracyjnego testu:

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>

To mówi Trade Federation, aby zainstalowała HelloWorldTests.apk na urządzeniu docelowym przy użyciu określonego target_preparer. Deweloperzy w Federacji Handlowej mają do dyspozycji wiele narzędzi do przygotowywania obiektów docelowych, które można wykorzystać do zapewnienia prawidłowej konfiguracji urządzenia 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ę testową Federacji Handlowej, która ma zostać użyta do wykonania testu i przechodzi w pakiecie na urządzeniu, które ma zostać wykonane, oraz środowisko do uruchamiania testów, którym w tym przypadku jest JUnit.

Aby uzyskać więcej informacji, zobacz Konfiguracje modułu testowego .

Funkcje JUnit4

Używanie biblioteki android-support-test jako programu uruchamiającego testy umożliwia przyjęcie nowych klas testowych w stylu JUnit4, a przykładowa zmiana gerrit zawiera bardzo podstawowe zastosowania jej funkcji. Zobacz przykład w /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java .

Podczas gdy wzorce testowania są zwykle specyficzne dla zespołów składowych, istnieje kilka ogólnie użytecznych wzorców użycia.

@RunWith(JUnit4.class)
public class HelloWorldTest {

Istotną różnicą w JUnit4 jest to, że testy nie muszą już dziedziczyć ze wspólnej podstawowej klasy testowej; zamiast tego piszesz testy w zwykłych klasach Java i używasz adnotacji do wskazania określonej konfiguracji testu i ograniczeń. W tym przykładzie instruujemy, ż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() {
    ...

@Before i @After są używane w metodach JUnit4 do przeprowadzania konfiguracji przed testem i usuwania po teście. Podobnie, adnotacje @BeforeClass i @AfterClass są używane w metodach JUnit4 do przeprowadzania konfiguracji przed wykonaniem wszystkich testów w klasie testowej, a następnie rozbierania. Należy zauważyć, że metody konfigurowania i niszczenia zakresu klas muszą być statyczne. Jeśli chodzi o metody testowe, w przeciwieństwie do wcześniejszych wersji JUnit, nie muszą już zaczynać nazwy metody od test , zamiast tego każda z nich musi być opatrzona adnotacją @Test . Jak zwykle, metody testowe muszą być publiczne, nie deklarować wartości zwracanej, nie przyjmować żadnych parametrów i mogą zgłaszać wyjątki.

Ważne : same metody testowe są opatrzone adnotacją @Test ; i zwróć uwagę, że aby testy mogły być wykonywane przez APCT, muszą być opatrzone adnotacjami o rozmiarach testów: przykładowa metoda z adnotacjami testHelloWorld jako @SmallTest . Adnotacja może być zastosowana w zakresie metody lub w zakresie klasy.

Dostęp do instrumentation

Chociaż nie jest to omówione w podstawowym przykładzie Hello World, dość często zdarza się, że test systemu Android wymaga dostępu do instancji Instrumentation : jest to podstawowy interfejs API, który zapewnia dostęp do kontekstów aplikacji, testowych interfejsów API związanych z cyklem życia aktywności i nie tylko.

Ponieważ testy JUnit4 nie wymagają już wspólnej klasy bazowej, nie jest już konieczne uzyskiwanie instancji Instrumentation za pomocą InstrumentationTestCase#getInstrumentation() , zamiast tego nowy program uruchamiający testy zarządza nią za pośrednictwem InstrumentationRegistry , w którym przechowywane są ustawienia kontekstowe i środowiskowe utworzone przez platformę Instrumentation.

Aby uzyskać dostęp do instancji klasy Instrumentation , wystarczy wywołać statyczną metodę getInstrumentation() w klasie InstrumentationRegistry :

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

Twórz i testuj lokalnie

W przypadku najczęstszych przypadków użycia zastosuj Atest .

W przypadku bardziej złożonych przypadków wymagających większego dostosowania postępuj zgodnie z instrukcjami dotyczącymi oprzyrządowania .