Używanie podwójnej precyzji testów na Androidzie

Podczas testowania elementu lub systemu elementów robisz to w izolacji. Na przykład, aby przetestować ViewModel, nie musisz uruchamiać emulatora ani uruchamiać interfejsu użytkownika, ponieważ nie zależy on (lub nie powinien) od platformy Android.

Skuteczność testu może jednak zależyć od innych. Na przykład ViewModel może działać tylko wtedy, gdy ma dostęp do repozytorium danych.

Jeśli musisz zapewnić zależność od testowanego tematu, zwykłą praktyką jest utworzenie testowego podwójnika (lub obiektu testowego). Podwójne obiekty testowe to obiekty, które wyglądają i działają jak komponenty w aplikacji, ale są tworzone w testach, aby zapewniać określone zachowanie lub dane. Główną zaletą jest to, że testy są szybsze i prostsze.

Rodzaje podwójnej precyzji testowej

Istnieją różne typy podwójnych testów:

Nieprawdziwe dane Podwójny plik testowy, który ma „działającą” implementację klasy, ale jest zaimplementowany w sposób, który sprawia, że sprawdza się on podczas testów, ale nie nadaje się do produkcji.

Przykład: baza danych działająca w pamięci.

Fake nie wymagają frameworka do mockowania i są lekkie. Są one preferowane.

Mock podwójny obiekt testowy, który zachowuje się zgodnie z programem i ma określone oczekiwania dotyczące interakcji; Próby nie zakończą testów, jeśli ich interakcje nie będą zgodne ze zdefiniowanymi przez Ciebie wymaganiami. Aby osiągnąć ten cel, w przykładach używa się zwykle platformy do tworzenia próbek.

Przykład: sprawdź, czy metoda w bazie danych została wywołana dokładnie raz.

Odcinek podwójny obiekt testowy, który zachowuje się zgodnie z programem, ale nie ma oczekiwań dotyczących interakcji; Zwykle są tworzone za pomocą frameworka do testowania. Ze względu na prostotę preferowane są fałszywe elementy zamiast elementów zastępczych.
pusty podwójna zmienna testowa, która jest przekazywana, ale nie jest używana, np. gdy musisz ją podać jako parametr;

Przykład: pusta funkcja przekazana jako wywołanie zwrotne kliknięcia.

Szpiegowski Otoczka na prawdziwym obiekcie, która śledzi również niektóre dodatkowe informacje, podobne do mock. Zwykle unika się ich ze względu na złożoność. Dlatego preferowane są fałszywe lub symulowane dane.
Cień Fałszywy użyty w Robolectric.

Przykład użycia fałszywego adresu

Załóżmy, że chcesz przeprowadzić test jednostkowy ViewModel, który zależy od interfejsu o nazwie UserRepository i wyświetla nazwę pierwszego użytkownika w interfejsie użytkownika. Możesz utworzyć fałszywy test podwójny, implementując interfejs i zwracając znane dane.

object FakeUserRepository : UserRepository {
    fun getUsers() = listOf(UserAlice, UserBob)
}

val const UserAlice = User("Alice")
val const UserBob = User("Bob")

Ta fałszywa wersja UserRepository nie musi być zależna od lokalnych i zdalnych źródeł danych, których używa wersja produkcyjna. Plik znajduje się w testowym zestawie źródeł i nie jest dostarczany z wersją produkcyjną aplikacji.

Fałszywa zależność może zwracać znane dane bez korzystania ze zdalnych źródeł danych
Ilustracja 1. Fałszywa zależność w teście jednostkowym.

Ten test sprawdza, czy ViewModel prawidłowo udostępnia widokowi pierwszą nazwę użytkownika.

@Test
fun viewModelA_loadsUsers_showsFirstUser() {
    // Given a VM using fake data
    val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init

    // Verify that the exposed data is correct
    assertEquals(viewModel.firstUserName, UserAlice.name)
}

Zastąpienie obiektu UserRepository obiektem fałszywym jest łatwe w przypadku testu jednostkowego, ponieważ widok modelu jest tworzony przez testera. Jednak w większych testach zastąpienie dowolnych elementów może być trudne.

Zastępowanie komponentów i wstrzykiwanie zależności

Gdy testy nie mają kontroli nad tworzeniem testowanych systemów, zastępowanie komponentów na potrzeby podwójnej precyzji staje się bardziej skomplikowane i wymaga, aby architektura aplikacji była zgodna z testowalną konstrukcją.

Nawet duże kompleksowe testy przyniosą korzyści ze stosowania podwójnej precyzji testującej, np. opartego interfejsu użytkownika, który pozwala poruszać się po całym przepływie użytkownika aplikacji. W takim przypadku warto ułożyć test w hermetyczny sposób. Test hermetyczny wyklucza wszystkie zależności zewnętrzne, takie jak pobieranie danych z internetu. Dzięki temu zwiększysz niezawodność i wydajność.

Rysunek 2. Duży test obejmujący większość aplikacji i fałszywe dane zdalne.

Możesz zaprojektować aplikację tak, aby uzyskać tę elastyczność ręcznie, ale zalecamy użycie frameworku wstrzykiwania zależności, takiego jak Hilt, aby zastąpić komponenty w aplikacji w czasie testów. Zapoznaj się z przewodnikiem po testowaniu Hilta.

Dalsze kroki

Na stronie Strategie testowania znajdziesz informacje o tym, jak zwiększyć produktywność za pomocą różnych typów testów.