Android Studio ułatwia proces testowania dzięki integracji z bibliotekami wspomagającymi testowanie oraz dostarczeniu dedykowanych narzędzi. Dodając odpowiednie zależności AndroidX do projektu już za pomocą kilku kliknięć można wykorzystać możliwości takich bibliotek jaki: JUnit, Mockito, Espresso, Robolectric czy UI Automator. Lokalne testy jednostkowe uruchamiane na maszynie lokalnej JVM znajdują się w lokalizacji: moduleName/src/test/java/ natomiast instrumentalne testy jednostkowe przeznaczone do uruchamiania na urządzeniu lub emulatorze znajdują się w: moduleName/src/androidTest/java/. W przypadku testów instrumentalnych przeznaczonych dla różnych wariantów budowania aplikacji ścieżka zmienia się na: moduleName/src/androidTestBuildVariantName/java/.
Tworzenie i uruchamianie
Testy jednostkowe można tworzyć dodając klasy do odpowiednich folderów lub za pomocą skrótu Ctrl+Shift+T kierując kursor na klasę lub metodę. Uruchamianie i analiza następuje z poziomu narzędzia GUI lub konsoli w Android Studio, które umożliwia m.in. eksport wyników testów, przeglądanie statystyk oraz pokrycie kodu.
Motywacja
Podstawą procesu testowania jest tworzenie i wykonywanie testów jednostkowych, które odpowiednio napisane w łatwy i wiarygodny sposób weryfikują poprawności logiki jednostki testowanej. Wykonywanie testów jednostkowych przy każdym przyroście ułatwia szybkie wyłapanie błędu. Należy jednak pamiętać o izolacji testowanej jednostki od pozostałych zależności. JUnit w Android jest wykorzystywany przede wszystkim do pisania lokalnych testów jednostkowych lub prostych testów instrumentalnych dla których można dostarczyć zależności (własne lub przy pomocy biblioteki np. Mockito). W pozostałych sytuacjach należy wykorzystać bibliotekę dostarczają zależności środowiska uruchomieniowego np. Robolectric. Tworzenie asercji może zostać usprawnione przez wykorzystanie Hamcrest. Alternatywą dla implementacji JUnit dla Android jest wykorzystanie biblioteki Truth we współpracy z asercjami Android.
Dobre praktyki
Tworząc metody testowe należy przede wszystkim pamiętać o wykluczeniu wszelkich zależności w taki sposób, aby na wynik testu testowanej jednostki nie miały wpływu inne zależności. Klasy testowe powinny znajdować się w pakiecie o tej samej nazwie co klasy implementacji, a nazwy klas testowych powinny być podobne do klas testowanych. Metody opisowe powinny być nazywane w sposób opisowy i jednoznaczny w nawiązaniu do celu testu nawet jeśli z tego powodu nazwa metody jest długa. W tym celu można posłużyć się konwencją Given/When/Then, gdzie Given określa warunki początkowe, When opisuje akcje, a Then informuje o oczekiwanym rezultacie. Ponadto należy dążyć do minimalizacji asercji, czasu wykonywania testów oraz zwiększać pokrycie kodu. Testy powinny być krótkie, proste i ściśle dotyczyć jednej jednostki. Jeśli sytuacja tego wymaga należy wykorzystywać metody cyklu życia testów, aby zapewnić odpowiednią inicjalizację i czyszczenie środowiska.
Przykład Klasa Game zarządza rozgrywką w grze komputerowej w której dwie przeciwne drużyny walczą o zwycięstwo zdobywając bramki. W odniesieniu do implementacji klasy Game zostaną przeprowadzone testy przedstawiające dobre praktyki oraz charakterystykę testów jednostkowych JUnit4 dla Android.
Klasa testowa
Testy w JUnit (metody) zawierają się w klasie testowej, które z kolei mogą być częścią zestawu klas testowych. Aby klasa była klasą testową w JUnit4 musi zawierać deklarację przynajmniej jednej metody testowej oznaczonej adnotacją @Test. Podstawowym elementem testów są asercje sprawdzające wartość logiczną, równość wartości czy referencji.
Cykl życia
Klasy testowe poza metodami testowymi mogą składać się także z metod inicjalizacyjnych i końcowych. Metoda oznaczona adnotacją @Before wykonywana jest przed każdym testem i służy przygotowaniu środowiska testowego, natomiast z adnotacją @After po każdym teście co wykorzystywane jest do czyszczenia środowiska. W analogiczny sposób działają metody oznaczone jako @BeforeClass oraz @AfterClass, które wykonują się kolejno przed i po uruchomieniu wszystkich testów. Jeśli metoda testowa ma zostać wyłączona z testów należy użyć adnotacji @Ignore. Adnotacja @Test może zostać wzbogacona o maksymalny czas wykonania (timeout) lub oczekiwany typ wyjątku.
Parametry
Klasa testowa może posiadać także jedną metodę generującą zestawy danych dla metod testowych. Klasa ta musi być oznaczona adnotacją @RunWith(Parameterized.class) natomiast metoda statyczna zwracająca kolekcje danych oznaczone jako @Parameters. Właściwości mogą przyjmować wstrzykniętą wartość za pomocą konstruktora lub być zdefiniowane w ciele klasy i oznaczone jako @Parameter(number).
Zasady
JUnit umożliwia dodawanie zachowania do każdego testu za pomocą adnotacji @Rule oraz tworzenie nowych zasad. Aby stworzyć własną zasadę należy w klasie zasady implementować interfejs TestRule.
AndroidX Test zawiera zestaw gotowych zasad dla JUnit, które zwiększają elastyczność, redukują powtarzający się kod oraz wspomagają testowanie komponentów Android. Wykorzystywane są przede wszystkim testach UI przy użyciu Espresso. ActivityTestRule dostarcza do klasy testowej żądanej Aktywności (Activity), która jest dostępna w całym cyklu życia klasy testowej, ServiceTestRule dostarcza Usługę (Service) natomiast IntentsTestRule dostarcza Intencję (Intent).
Przykład Aktywność GameActivity umożliwia użytkownikowi prowadzenie rozgrywki opierając się na klasie Game co może wprowadzać potrzebę przetestowania klasy Game z poziomu aplikacji. W tym celu należy stworzyć testy instrumentalne (w pakieckie androidTest) wykonywane przez AndroidJUnit4 i wykorzystać ActivityTestRule (lub bibliotekę Robolectric).
Filtry
AndroidJUnitRunner umożliwia stosowanie adnotacji dla testów instrumentalnych w celach restrykcyjnych. Adnotacja @RequiresDevice mówi, że test powinien zostać przeprowadzony tylko na urządzeniu fizycznym, @SdkSupress określa minimalne API natomiast @SmallTest, @MediumTest i @LargeTest informują o wielkości testu co przekłada się na czas jego wykonania.