Na tej stronie omawiamy podstawowe założenia testowania aplikacji na Androida, w tym najważniejsze sprawdzone metody i ich zalety.
Zalety testowania
Testowanie jest nieodłączną częścią procesu tworzenia aplikacji. Regularnie przeprowadzając testy aplikacji, możesz sprawdzić jej poprawność, działanie i użyteczność, zanim opublikujesz ją publicznie.
Możesz przetestować aplikację ręcznie, poruszając się po niej. Możesz używać różnych urządzeń i emulatorów, zmieniać język systemu i próbować generować wszystkie błędy u użytkowników lub przechodzić przez każdy proces.
Testy ręczne są jednak niezadowalające i łatwo przeoczyć regresje w działaniu aplikacji. Testy automatyczne obejmują korzystanie z narzędzi, które wykonują testy za Ciebie. Jest to szybsze, bardziej powtarzalne i ogólnie przekazuje więcej przydatnych informacji o aplikacji na wcześniejszym etapie procesu programowania.
Rodzaje testów na Androidzie
Aplikacje mobilne są złożone i muszą dobrze działać w wielu środowiskach. Jest wiele rodzajów testów.
Temat
Na przykład dostępne są różne rodzaje testów w zależności od tematu:
- Testy funkcjonalne: czy moja aplikacja działa tak, jak powinna?
- Testy wydajności: czy robi to szybko i skutecznie?
- Testowanie ułatwień dostępu: czy dobrze działa z usługami ułatwień dostępu?
- Testy zgodności: czy działa on dobrze na każdym urządzeniu i na każdym poziomie interfejsu API?
Zakres
Testy różnią się też w zależności od rozmiaru lub stopnia izolacji:
- Testy jednostkowe lub niewielkie testy służą do weryfikacji tylko niewielkiej części aplikacji, takiej jak metoda lub klasa.
- Testy kompleksowe lub duże testy pozwalają w tym samym czasie zweryfikować większe części aplikacji, np. cały ekran lub wzorzec przeglądania.
- Testy średnie znajdują się w fazie testów i sprawdź integrację między co najmniej 2 jednostkami.
![Testy mogą być małe, średnie lub duże.](https://developer.android.google.cn/static/training/testing/fundamentals/test-scopes.png?authuser=6&hl=pl)
Testy można klasyfikować na wiele sposobów. Najważniejszą różnicą dla deweloperów aplikacji jest miejsce, w którym przeprowadza się testy.
Testy instrumentowane i lokalne
Testy możesz przeprowadzić na urządzeniu z Androidem lub na innym komputerze:
- Testy instrumentalne są przeprowadzane na urządzeniu z Androidem (fizyczne lub emulowane). Aplikacja jest kompilowana i instalowana razem z aplikacją testową, która wstrzykuje polecenia i odczytuje stan. Są to zwykle testy interfejsu, podczas których uruchamiasz aplikację i wchodzisz z nią w interakcję.
- Testy lokalne są wykonywane na komputerze programisty lub na serwerze. Są one też nazywane testami po stronie hosta. Zwykle są małe i szybkie, izolujące obiekt od reszty aplikacji.
![Testy mogą być przeprowadzane na urządzeniu w ramach testów z instrumentacją lub lokalnie na komputerze programistycznym.](https://developer.android.google.cn/static/training/testing/fundamentals/instru-vs-local.png?authuser=6&hl=pl)
Nie wszystkie testy jednostkowe mają charakter lokalny i nie wszystkie kompleksowe testy są przeprowadzane na danym urządzeniu. Przykład:
- Duży test lokalny: możesz użyć symulatora Androida, który działa lokalnie, np. Robolectric.
- Mały test z użyciem instrumentów: możesz sprawdzić, czy kod działa dobrze z funkcją platformy, np. z bazą danych SQLite. Możesz uruchomić ten test na wielu urządzeniach, aby sprawdzić integrację z wieloma wersjami SQLite.
Przykłady
Poniższe fragmenty kodu pokazują, jak korzystać z interfejsu w instrumentalnym teście interfejsu, który polega na kliknięciu elementu i sprawdzaniu, czy się wyświetla inny element.
Espresso
// When the Continue button is clicked
onView(withText("Continue"))
.perform(click())
// Then the Welcome screen is displayed
onView(withText("Welcome"))
.check(matches(isDisplayed()))
Interfejs tworzenia
// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()
// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()
Ten fragment kodu pokazuje część testu jednostkowego modelu ViewModel (lokalnego testu po stronie hosta):
// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)
// When data is loaded
viewModel.loadData()
// Then it should be exposing data
assertTrue(viewModel.data != null)
Definiowanie strategii testowania
W idealnym świecie należałoby przetestować każdy wiersz kodu w aplikacji na każdym urządzeniu, z którym jest ona zgodna. Niestety, jest to zbyt powolne i kosztowne, by zastosować je w praktyce.
Dobra strategia testowania pozwala znaleźć odpowiednią równowagę między wiernością testu, jego szybkością i niezawodnością. O rzetelności testu decyduje podobieństwo środowiska testowego do prawdziwego urządzenia. Testy o wyższej wierności są przeprowadzane na emulowanych urządzeniach lub na samym urządzeniu fizycznym. Testy o niższej wierności mogą być przeprowadzane na maszynie JVM lokalnej stacji roboczej. Testy wysokiej jakości są często wolniejsze i wymagają większej ilości zasobów, więc nie każdy test powinien być skutecznym testem.
Niepewne wyniki testów
Błędy występują nawet w przypadku poprawnie zaprojektowanych i zaimplementowanych uruchomień testowych. Jeśli na przykład przeprowadzasz test na prawdziwym urządzeniu, automatyczna aktualizacja może się rozpocząć w środku testu i sprawić, że zakończy się niepowodzeniem. Subtelne warunki wyścigu w kodzie mogą występować tylko w niewielkim stopniu. Testy, które nie zostaną zaliczone w 100% czasu, wychodzą niestabilnie.
Architektura z możliwością przetestowania
W testowej architekturze aplikacji kod jest zgodny ze strukturą, która umożliwia łatwe testowanie różnych części aplikacji z osobna. Architektury testujące mają też inne zalety, np. lepszą czytelność, łatwość obsługi, skalowalność i możliwość wielokrotnego użytku.
Architektura, której nie można przetestować, sprawia, że:
- Większe, wolniejsze i bardziej niestabilne testy. Klasy, których nie da się przetestować w ramach testów jednostkowych, mogą wymagać częstszych testów integracji lub testów interfejsu.
- Mniej możliwości testowania różnych scenariuszy. Większe testy są wolniejsze, więc testowanie wszystkich możliwych stanów aplikacji może być nierealistyczne.
Więcej informacji o wytycznych dotyczących architektury znajdziesz w przewodniku po architekturze aplikacji.
Sposoby na odłączanie
Jeśli uda Ci się wyodrębnić część funkcji, klasy lub modułu z reszty, testowanie jest łatwiejsze i skuteczniejsze. Nazywamy to rozłączaniem i jest to koncepcja, która jest najważniejsza dla testowalnej architektury.
Typowe metody rozłączania danych to:
- Podziel aplikację na warstwy, takie jak Prezentacja, Domena i Dane. Możesz też podzielić aplikację na moduły, po jednym na funkcję.
- Unikaj dodawania logiki do encji, które mają duże zależności, np. w działaniach i fragmentach. Użyj tych klas jako punktów wejścia do platformy i przenieś logikę interfejsu użytkownika i logikę biznesową w inne miejsca, np. do warstwy Composable, ViewModel lub warstwy domeny.
- Unikaj bezpośrednich zależności platformy w klasach zawierających logikę biznesową. Na przykład nie używaj kontekstu Androida w obiektach ViewModels.
- Ułatw zastępowanie zależności. Na przykład używaj interfejsów zamiast konkretnych implementacji. Użyj funkcji wstrzykiwania zależności, nawet jeśli nie korzystasz z platformy DI.
Dalsze kroki
Wiesz już, dlaczego warto przeprowadzać testy i jakie są 2 główne typy testów. Teraz przeczytaj artykuł Co testować.
Jeśli chcesz utworzyć pierwszy test i wyciągnąć wnioski, zapoznaj się z ćwiczeniami z programowania dotyczącymi testowania.