Te warsztaty są częścią kursu Zaawansowany Android w Kotlinie. Najwięcej korzyści z tego kursu uzyskasz, jeśli przejdziesz wszystkie ćwiczenia w kolejności, ale nie jest to obowiązkowe. Wszystkie ćwiczenia z tego kursu znajdziesz na stronie docelowej ćwiczeń z zaawansowanego Androida w Kotlinie.
Wprowadzenie
Gdy wdrażasz pierwszą funkcję pierwszej aplikacji, prawdopodobnie uruchamiasz kod, aby sprawdzić, czy działa zgodnie z oczekiwaniami. Przeprowadzono test, ale był to test ręczny. W miarę dodawania i aktualizowania funkcji prawdopodobnie nadal uruchamiasz kod i sprawdzasz, czy działa. Jednak ręczne wykonywanie tej czynności za każdym razem jest męczące, podatne na błędy i nie pozwala na skalowanie.
Komputery świetnie radzą sobie ze skalowaniem i automatyzacją. Dlatego programiści w małych i dużych firmach piszą testy automatyczne, czyli testy, które są uruchamiane przez oprogramowanie i nie wymagają ręcznego obsługiwania aplikacji w celu sprawdzenia, czy kod działa.
Z tej serii ćwiczeń dowiesz się, jak utworzyć zbiór testów (nazywany pakietem testów) dla rzeczywistej aplikacji.
W tym pierwszym laboratorium dowiesz się, jak testować aplikacje na Androida. Napiszesz pierwsze testy i nauczysz się testować LiveData
i ViewModel
.
Co warto wiedzieć
Musisz znać:
- Język programowania Kotlin
- Te podstawowe biblioteki Androida Jetpack:
ViewModel
iLiveData
. - Architektura aplikacji zgodna ze wzorcem z przewodnika po architekturze aplikacji i kursów podstawowych dotyczących Androida
Czego się nauczysz
Dowiesz się z niego:
- Jak pisać i uruchamiać testy jednostkowe na Androidzie
- Jak korzystać z programowania opartego na testach
- Wybieranie testów z instrumentacją i testów lokalnych
Poznasz te biblioteki i koncepcje związane z kodem:
- JUnit4
- Hamcrest
- Biblioteka testowa AndroidX
- Biblioteka testowa AndroidX Architecture Components Core
Jakie zadania wykonasz
- Konfigurowanie, przeprowadzanie i interpretowanie testów lokalnych i instrumentalnych na Androidzie.
- Pisać testy jednostkowe na Androidzie za pomocą JUnit4 i Hamcrest.
- Napisz proste testy
LiveData
iViewModel
.
W tej serii ćwiczeń będziesz pracować z aplikacją TO-DO Notes. Umożliwia ona zapisywanie zadań do wykonania i wyświetlanie ich na liście. Możesz je oznaczać jako ukończone lub nieukończone, filtrować lub usuwać.
Ta aplikacja jest napisana w języku Kotlin, ma kilka ekranów, korzysta z komponentów Jetpack i jest zgodna z architekturą opisaną w przewodniku po architekturze aplikacji. Dzięki temu, że dowiesz się, jak testować tę aplikację, będziesz w stanie testować aplikacje, które korzystają z tych samych bibliotek i architektury.
Aby rozpocząć, pobierz kod:
Możesz też sklonować repozytorium GitHub, aby uzyskać kod:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout starter_code
W tym zadaniu uruchomisz aplikację i zapoznasz się z bazą kodu.
Krok 1. Uruchom przykładową aplikację
Po pobraniu aplikacji TO-DO otwórz ją w Android Studio i uruchom. Powinien się skompilować. Poznaj aplikację, wykonując te czynności:
- Utwórz nowe zadanie za pomocą pływającego przycisku czynności. Najpierw wpisz tytuł, a potem dodatkowe informacje o zadaniu. Zapisz go za pomocą pływającego przycisku czynności z zielonym znacznikiem wyboru.
- Na liście zadań kliknij tytuł zadania, które właśnie zostało ukończone, i sprawdź ekran szczegółów, aby zobaczyć resztę opisu.
- Na liście lub na ekranie szczegółów zaznacz pole wyboru tego zadania, aby ustawić jego stan na Ukończone.
- Wróć do ekranu zadań, otwórz menu filtra i filtruj zadania według stanu Aktywne i Ukończone.
- Otwórz panel nawigacji i kliknij Statystyki.
- Wróć do ekranu przeglądu i w menu panelu nawigacyjnego wybierz Wyczyść ukończone, aby usunąć wszystkie zadania ze stanem Ukończone.
Krok 2. Zapoznaj się z kodem przykładowej aplikacji
Aplikacja TO-DO jest oparta na popularnym przykładzie testowym i architektonicznym Architecture Blueprints (w wersji architektury reaktywnej). Aplikacja jest zgodna z architekturą opisaną w przewodniku po architekturze aplikacji. Korzysta z obiektów ViewModel z fragmentami, repozytorium i biblioteką Room. Jeśli znasz któryś z poniższych przykładów, ta aplikacja ma podobną architekturę:
- Ćwiczenia z programowania Room with a View
- Ćwiczenia z podstaw języka Kotlin na Androidzie
- Zaawansowane szkolenia z programowania aplikacji na Androida
- Przykładowa aplikacja Sunflower na Androida
- Kurs Developing Android Apps with Kotlin w Udacity
Ważniejsze jest zrozumienie ogólnej architektury aplikacji niż dogłębne poznanie logiki na dowolnej warstwie.
Oto podsumowanie pakietów, które znajdziesz:
Pakiet: | |
| Ekran dodawania lub edytowania zadania: kod warstwy interfejsu użytkownika do dodawania lub edytowania zadania. |
| Warstwa danych: dotyczy warstwy danych zadań. Zawiera kod bazy danych, sieci i repozytorium. |
| Ekran statystyk: kod warstwy interfejsu ekranu statystyk. |
| Ekran szczegółów zadania: kod warstwy interfejsu pojedynczego zadania. |
| Ekran zadań: kod warstwy interfejsu użytkownika dla listy wszystkich zadań. |
| Klasy narzędziowe: klasy udostępnione używane w różnych częściach aplikacji, np. w układzie odświeżania przez przesunięcie, który jest używany na wielu ekranach. |
Warstwa danych (.data)
Ta aplikacja zawiera symulowaną warstwę sieciową w pakiecie remote i warstwę bazy danych w pakiecie local. W tym projekcie warstwa sieci jest symulowana za pomocą funkcji HashMap
z opóźnieniem, a nie za pomocą rzeczywistych żądań sieciowych.
DefaultTasksRepository
koordynuje lub pośredniczy między warstwą sieciową a warstwą bazy danych i zwraca dane do warstwy interfejsu.
Warstwa interfejsu ( .addedittask, .statistics, .taskdetail, .tasks)
Każdy z pakietów warstwy interfejsu zawiera fragment i model widoku oraz inne klasy wymagane w interfejsie (np. adapter listy zadań). TaskActivity
to aktywność, która zawiera wszystkie fragmenty.
Nawigacja
Nawigacja w aplikacji jest kontrolowana przez komponent nawigacji. Jest on zdefiniowany w pliku nav_graph.xml
. Nawigacja jest wywoływana w modelach widoku za pomocą klasy Event
. Modele widoku określają też, jakie argumenty mają być przekazywane. Fragmenty obserwują Event
i wykonują rzeczywistą nawigację między ekranami.
W tym zadaniu przeprowadzisz pierwsze testy.
- W Android Studio otwórz panel Projekt i znajdź te 3 foldery:
com.example.android.architecture.blueprints.todoapp
com.example.android.architecture.blueprints.todoapp (androidTest)
com.example.android.architecture.blueprints.todoapp (test)
Te foldery są nazywane zestawami źródeł. Zbiory źródeł to foldery zawierające kod źródłowy aplikacji. Zbiory źródeł, które są oznaczone kolorem zielonym (androidTest i test), zawierają testy. Gdy tworzysz nowy projekt na Androida, domyślnie otrzymujesz te 3 zbiory źródeł: Są to:
main
: zawiera kod aplikacji. Ten kod jest wspólny dla wszystkich wersji aplikacji, które możesz utworzyć (tzw. wariantów kompilacji).androidTest
: zawiera testy określane jako testy z użyciem instrumentacji.test
: zawiera testy lokalne.
Różnica między testami lokalnymi a testami z użyciem instrumentacji polega na sposobie ich przeprowadzania.
Testy lokalne (test
zbiór źródeł)
Te testy są uruchamiane lokalnie w JVM na komputerze deweloperskim i nie wymagają emulatora ani urządzenia fizycznego. Dlatego działają szybko, ale ich wierność jest mniejsza, co oznacza, że zachowują się mniej realistycznie.
W Android Studio testy lokalne są oznaczone zielono-czerwoną ikoną trójkąta.
Testy z instrumentacją (androidTest
zbiór źródeł)
Testy te są przeprowadzane na rzeczywistych lub emulowanych urządzeniach z Androidem, więc odzwierciedlają to, co się stanie w rzeczywistości, ale są też znacznie wolniejsze.
W Android Studio testy z instrumentacją są reprezentowane przez ikonę Androida z zielonym i czerwonym trójkątem.
Krok 1. Przeprowadź test lokalny
- Otwórz folder
test
, aż znajdziesz plik ExampleUnitTest.kt. - Kliknij go prawym przyciskiem myszy i wybierz Run ExampleUnitTest (Uruchom ExampleUnitTest).
W oknie Uruchom u dołu ekranu powinny się wyświetlić te dane wyjściowe:
- Zwróć uwagę na zielone ikony potwierdzenia i rozwiń wyniki testu, aby sprawdzić, czy jeden z testów o nazwie
addition_isCorrect
został zaliczony. Cieszę się, że dodawanie działa zgodnie z oczekiwaniami.
Krok 2. Spraw, aby test się nie powiódł
Poniżej znajdziesz test, który został właśnie przeprowadzony.
ExampleUnitTest.kt
// A test class is just a normal class
class ExampleUnitTest {
// Each test is annotated with @Test (this is a Junit annotation)
@Test
fun addition_isCorrect() {
// Here you are checking that 4 is the same as 2+2
assertEquals(4, 2 + 2)
}
}
Zwróć uwagę, że testy
- należą do klasy w jednym z testowych zestawów źródeł.
- zawierają funkcje, które zaczynają się od adnotacji
@Test
(każda funkcja to pojedynczy test); - zwykle zawierają instrukcje asercji.
Android używa biblioteki testowej JUnit (w tym ćwiczeniu z programowania JUnit4). Zarówno asercje, jak i adnotacja @Test
pochodzą z JUnit.
Asercja jest podstawą testu. Jest to instrukcja kodu, która sprawdza, czy kod lub aplikacja działały zgodnie z oczekiwaniami. W tym przypadku asercja to assertEquals(4, 2 + 2)
, która sprawdza, czy 4 równa się 2 + 2.
Aby zobaczyć, jak wygląda nieudany test, dodaj asercję, która z łatwością powinna zakończyć się niepowodzeniem. Sprawdzi, czy 3 = 1+1.
- Dodaj
assertEquals(3, 1 + 1)
do testuaddition_isCorrect
.
ExampleUnitTest.kt
class ExampleUnitTest {
// Each test is annotated with @Test (this is a Junit annotation)
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
assertEquals(3, 1 + 1) // This should fail
}
}
- Przeprowadź test.
- W wynikach testu zobaczysz znak X obok testu.
- Zwróć też uwagę na:
- Jeśli nie powiedzie się jedno sprawdzenie, cały test zakończy się niepowodzeniem.
- Wyświetlona zostanie oczekiwana wartość (3) i wartość, która została faktycznie obliczona (2).
- Przekierujemy Cię do wiersza nieudanej asercji
(ExampleUnitTest.kt:16)
.
Krok 3. Przeprowadź test z instrumentacją
Testy instrumentowane znajdują się w androidTest
zbiorze źródeł.
- Otwórz zestaw źródeł
androidTest
. - Uruchom test o nazwie
ExampleInstrumentedTest
.
ExampleInstrumentedTest
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.android.architecture.blueprints.reactive",
appContext.packageName)
}
}
W przeciwieństwie do testu lokalnego ten test jest przeprowadzany na urządzeniu (w poniższym przykładzie na emulowanym telefonie Pixel 2):
Jeśli masz podłączone urządzenie lub uruchomiony emulator, test powinien zostać przeprowadzony na emulatorze.
W tym zadaniu napiszesz testy dla funkcji getActiveAndCompleteStats
, która oblicza odsetek aktywnych i ukończonych zadań w statystykach aplikacji. Te liczby możesz zobaczyć na ekranie statystyk aplikacji.
Krok 1. Utwórz klasę testową
- W zbiorze źródłowym
main
wtodoapp.statistics
otwórzStatisticsUtils.kt
. - Znajdź funkcję
getActiveAndCompletedStats
.
StatisticsUtils.kt
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
val totalTasks = tasks!!.size
val numberOfActiveTasks = tasks.count { it.isActive }
val activePercent = 100 * numberOfActiveTasks / totalTasks
val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks
return StatsResult(
activeTasksPercent = activePercent.toFloat(),
completedTasksPercent = completePercent.toFloat()
)
}
data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float)
Funkcja getActiveAndCompletedStats
przyjmuje listę zadań i zwraca StatsResult
. StatsResult
to klasa danych, która zawiera 2 liczby: odsetek zadań ukończonych i odsetek zadań aktywnych.
Android Studio udostępnia narzędzia do generowania testowych elementów zastępczych, które pomagają wdrażać testy tej funkcji.
- Kliknij prawym przyciskiem myszy
getActiveAndCompletedStats
i wybierz Wygeneruj > Test.
Otworzy się okno Utwórz test:
- Zmień Nazwa zajęć: na
StatisticsUtilsTest
(zamiastStatisticsUtilsKtTest
; lepiej, żeby nazwa zajęć testowych nie zawierała KT). - Zachowaj pozostałe ustawienia domyślne. JUnit 4 to odpowiednia biblioteka testowa. Pakiet docelowy jest prawidłowy (odzwierciedla lokalizację klasy
StatisticsUtils
) i nie musisz zaznaczać żadnych pól wyboru (generuje to tylko dodatkowy kod, ale test napiszesz od zera). - Kliknij OK.
Otworzy się okno Wybierz katalog docelowy:
Przeprowadzisz test lokalny, ponieważ funkcja wykonuje obliczenia matematyczne i nie zawiera żadnego kodu specyficznego dla Androida. Nie musisz więc uruchamiać go na prawdziwym ani emulowanym urządzeniu.
- Wybierz katalog
test
(nieandroidTest
), ponieważ będziesz pisać testy lokalne. - Kliknij OK.
- Zwróć uwagę na wygenerowaną klasę
StatisticsUtilsTest
wtest/statistics/
.
Krok 2. Napisz pierwszą funkcję testową
Napiszesz test, który sprawdzi:
- jeśli nie ma ukończonych zadań i jest jedno aktywne zadanie;
- odsetek aktywnych testów wynosi 100%,
- a procent ukończonych zadań wynosi 0%.
- Otwórz pokój
StatisticsUtilsTest
. - Utwórz funkcję o nazwie
getActiveAndCompletedStats_noCompleted_returnsHundredZero
.
StatisticsUtilsTest.kt
class StatisticsUtilsTest {
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task
// Call your function
// Check the result
}
}
- Dodaj adnotację
@Test
nad nazwą funkcji, aby wskazać, że jest to test. - Utwórz listę zadań.
// Create an active task
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
- Wywołaj
getActiveAndCompletedStats
z tymi zadaniami.
// Call your function
val result = getActiveAndCompletedStats(tasks)
- Sprawdź, czy wartość
result
jest zgodna z oczekiwaniami, używając asercji.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
Oto pełny kod.
StatisticsUtilsTest.kt
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task (the false makes this active)
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
// Call your function
val result = getActiveAndCompletedStats(tasks)
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
}
}
- Uruchom test (kliknij prawym przyciskiem myszy
StatisticsUtilsTest
i wybierz Uruchom).
Powinien przejść:
Krok 3. Dodaj zależność Hamcrest
Testy działają jak dokumentacja tego, co robi Twój kod, więc dobrze, jeśli są czytelne dla ludzi. Porównaj te 2 twierdzenia:
assertEquals(result.completedTasksPercent, 0f)
// versus
assertThat(result.completedTasksPercent, `is`(0f))
Drugie stwierdzenie brzmi bardziej jak zdanie wypowiedziane przez człowieka. Jest on napisany przy użyciu platformy asercji o nazwie Hamcrest. Kolejnym dobrym narzędziem do pisania czytelnych asercji jest biblioteka Truth. W tym ćwiczeniu do pisania asercji będziesz używać biblioteki Hamcrest.
- Otwórz
build.grade (Module: app)
i dodaj tę zależność.
app/build.gradle
dependencies {
// Other dependencies
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}
Zwykle podczas dodawania zależności używasz implementation
, ale tutaj używasz testImplementation
. Gdy będziesz gotowy(-a) do udostępnienia aplikacji całemu światu, nie powiększaj rozmiaru pliku APK za pomocą kodu testowego ani zależności w aplikacji. Możesz określić, czy biblioteka ma być uwzględniona w kodzie głównym czy w kodzie testowym, za pomocą konfiguracji Gradle. Najczęstsze konfiguracje to:
implementation
– Zależność jest dostępna we wszystkich zestawach źródeł, w tym w zestawach źródeł testowych.testImplementation
– zależność jest dostępna tylko w zbiorze źródeł testowych.androidTestImplementation
– zależność jest dostępna tylko wandroidTest
zbiorze źródeł.
Używana konfiguracja określa, gdzie można używać zależności. Jeśli napiszesz:
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
Oznacza to, że biblioteka Hamcrest będzie dostępna tylko w zestawie źródeł testowych. Dzięki temu biblioteka Hamcrest nie będzie uwzględniona w końcowej wersji aplikacji.
Krok 4. Użyj Hamcrest do pisania asercji
- Zaktualizuj test
getActiveAndCompletedStats_noCompleted_returnsHundredZero()
, aby zamiastassertEquals
używać funkcjiassertThat
biblioteki Hamcrest.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
W razie potrzeby możesz użyć opcji importowania import org.hamcrest.Matchers.`is`
.
Ostateczny test będzie wyglądał jak kod poniżej.
StatisticsUtilsTest.kt
import com.example.android.architecture.blueprints.todoapp.data.Task
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
// Create an active tasks (the false makes this active)
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
// Call your function
val result = getActiveAndCompletedStats(tasks)
// Check the result
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
}
}
- Uruchom zaktualizowany test, aby sprawdzić, czy nadal działa.
Te ćwiczenia z programowania nie nauczą Cię wszystkich szczegółów Hamcrest, więc jeśli chcesz dowiedzieć się więcej, zapoznaj się z oficjalnym samouczkiem.
To zadanie jest opcjonalne i ma charakter ćwiczenia.
W tym zadaniu napiszesz więcej testów za pomocą JUnit i Hamcrest. Napiszesz też testy, korzystając ze strategii wywodzącej się z praktyki programowania Test Driven Development. Programowanie sterowane testami (TDD) to szkoła programowania, która mówi, że zamiast najpierw pisać kod funkcji, najpierw piszesz testy. Następnie piszesz kod funkcji, aby przejść testy.
Krok 1. Pisanie testów
Napisz testy dla sytuacji, w których masz standardową listę zadań:
- Jeśli jest jedno ukończone zadanie i nie ma aktywnych zadań, wartość procentowa
activeTasks
powinna wynosić0f
, a wartość procentowa ukończonych zadań powinna wynosić100f
. - Jeśli są 2 ukończone zadania i 3 aktywne, odsetek ukończonych zadań powinien wynosić
40f
, a odsetek aktywnych –60f
.
Krok 2. Pisanie testu na błąd
Kod getActiveAndCompletedStats
zawiera błąd. Zwróć uwagę, że nie obsługuje on prawidłowo sytuacji, w której lista jest pusta lub ma wartość null. W obu tych przypadkach oba odsetki powinny wynosić zero.
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
val totalTasks = tasks!!.size
val numberOfActiveTasks = tasks.count { it.isActive }
val activePercent = 100 * numberOfActiveTasks / totalTasks
val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks
return StatsResult(
activeTasksPercent = activePercent.toFloat(),
completedTasksPercent = completePercent.toFloat()
)
}
Aby naprawić kod i napisać testy, użyjesz programowania sterowanego testami. Programowanie sterowane testami przebiega w tych krokach.
- Napisz test, używając struktury „Given, When, Then” (Zakładając, że… gdy… to…), i nadaj mu nazwę zgodną z konwencją.
- Potwierdź, że test się nie powiódł.
- Napisz minimalny kod, aby test zakończył się powodzeniem.
- Powtórz te czynności dla wszystkich testów.
Zamiast zaczynać od naprawienia błędu, najpierw napiszesz testy. Następnie możesz potwierdzić, że masz testy, które chronią Cię przed przypadkowym ponownym wprowadzeniem tych błędów w przyszłości.
- Jeśli lista jest pusta (
emptyList()
), oba odsetki powinny wynosić 0f. - Jeśli podczas wczytywania zadań wystąpił błąd, lista będzie pusta
null
, a oba odsetki powinny wynosić 0. - Uruchom testy i sprawdź, czy zakończyły się niepowodzeniem:
Krok 3. Naprawianie błędu
Teraz, gdy masz już testy, napraw błąd.
- Popraw błąd w funkcji
getActiveAndCompletedStats
, zwracając0f
, jeślitasks
ma wartośćnull
lub jest pusta:
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
return if (tasks == null || tasks.isEmpty()) {
StatsResult(0f, 0f)
} else {
val totalTasks = tasks.size
val numberOfActiveTasks = tasks.count { it.isActive }
StatsResult(
activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
)
}
}
- Uruchom testy ponownie i sprawdź, czy wszystkie zostały zaliczone.
Stosując TDD i pisząc testy w pierwszej kolejności, pomagasz zapewnić, że:
- Nowe funkcje zawsze mają powiązane testy, dzięki czemu testy działają jak dokumentacja tego, co robi Twój kod.
- Testy sprawdzają, czy wyniki są prawidłowe, i chronią przed błędami, które już wystąpiły.
Rozwiązanie: pisanie większej liczby testów
Oto wszystkie testy i odpowiadające im kody funkcji.
StatisticsUtilsTest.kt
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
val tasks = listOf(
Task("title", "desc", isCompleted = false)
)
// When the list of tasks is computed with an active task
val result = getActiveAndCompletedStats(tasks)
// Then the percentages are 100 and 0
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
}
@Test
fun getActiveAndCompletedStats_noActive_returnsZeroHundred() {
val tasks = listOf(
Task("title", "desc", isCompleted = true)
)
// When the list of tasks is computed with a completed task
val result = getActiveAndCompletedStats(tasks)
// Then the percentages are 0 and 100
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(100f))
}
@Test
fun getActiveAndCompletedStats_both_returnsFortySixty() {
// Given 3 completed tasks and 2 active tasks
val tasks = listOf(
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = false),
Task("title", "desc", isCompleted = false)
)
// When the list of tasks is computed
val result = getActiveAndCompletedStats(tasks)
// Then the result is 40-60
assertThat(result.activeTasksPercent, `is`(40f))
assertThat(result.completedTasksPercent, `is`(60f))
}
@Test
fun getActiveAndCompletedStats_error_returnsZeros() {
// When there's an error loading stats
val result = getActiveAndCompletedStats(null)
// Both active and completed tasks are 0
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(0f))
}
@Test
fun getActiveAndCompletedStats_empty_returnsZeros() {
// When there are no tasks
val result = getActiveAndCompletedStats(emptyList())
// Both active and completed tasks are 0
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(0f))
}
}
StatisticsUtils.kt
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
return if (tasks == null || tasks.isEmpty()) {
StatsResult(0f, 0f)
} else {
val totalTasks = tasks.size
val numberOfActiveTasks = tasks.count { it.isActive }
StatsResult(
activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
)
}
}
Świetnie, że znasz już podstawy pisania i przeprowadzania testów. Następnie dowiesz się, jak pisać podstawowe testy ViewModel
i LiveData
.
W dalszej części tego laboratorium dowiesz się, jak pisać testy dla 2 klas Androida, które są powszechne w większości aplikacji – ViewModel
i LiveData
.
Zacznij od napisania testów dla TasksViewModel
.
Skupisz się na testach, których cała logika znajduje się w modelu widoku i nie zależy od kodu repozytorium. Kod repozytorium obejmuje kod asynchroniczny, bazy danych i wywołania sieciowe, co zwiększa złożoność testów. Na razie tego unikniesz i skupisz się na pisaniu testów funkcji ViewModel, które nie testują bezpośrednio niczego w repozytorium.
Napisany przez Ciebie test sprawdzi, czy po wywołaniu metody addNewTask
uruchamiany jest Event
otwierający nowe okno zadania. Oto kod aplikacji, którą będziesz testować.
TasksViewModel.kt
fun addNewTask() {
_newTaskEvent.value = Event(Unit)
}
Krok 1. Utwórz klasę TasksViewModelTest
Wykonaj te same czynności co w przypadku StatisticsUtilTest
. W tym kroku utworzysz plik testowy dla TasksViewModelTest
.
- Otwórz zajęcia, które chcesz przetestować, w pakiecie
tasks
TasksViewModel.
. - W kodzie kliknij prawym przyciskiem myszy nazwę klasy
TasksViewModel
–> Wygeneruj –> Test.
- Na ekranie Utwórz test kliknij OK, aby zaakceptować ustawienia domyślne (nie musisz ich zmieniać).
- W oknie Wybierz katalog docelowy wybierz katalog test.
Krok 2. Rozpocznij pisanie testu ViewModel
W tym kroku dodasz test modelu widoku, aby sprawdzić, czy po wywołaniu metody addNewTask
uruchamia się Event
otwierający nowe okno zadania.
- Utwórz nowy test o nazwie
addNewTask_setsNewTaskEvent
.
TasksViewModelTest.kt
class TasksViewModelTest {
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh TasksViewModel
// When adding a new task
// Then the new task event is triggered
}
}
A co z kontekstem aplikacji?
Gdy tworzysz instancję TasksViewModel
na potrzeby testów, jej konstruktor wymaga kontekstu aplikacji. W tym teście nie tworzysz jednak pełnej aplikacji z aktywnościami, interfejsem i fragmentami. Jak więc uzyskać kontekst aplikacji?
TasksViewModelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(???)
Biblioteki testowe AndroidX zawierają klasy i metody, które udostępniają wersje komponentów takich jak aplikacje i aktywności przeznaczone do testów. Jeśli przeprowadzasz test lokalny, w którym potrzebujesz symulowanych klas platformy Android(np. kontekstu aplikacji), wykonaj te czynności, aby prawidłowo skonfigurować AndroidX Test:
- Dodaj podstawowe i zewnętrzne zależności AndroidX Test.
- Dodaj zależność biblioteki testowej Robolectric.
- Dodaj do klasy adnotację mechanizmu uruchamiania testów AndroidJUnit4.
- Pisanie kodu testu AndroidX
Wykonaj te czynności, a potem dowiesz się, jak działają razem.
Krok 3. Dodawanie zależności Gradle
- Skopiuj te zależności do pliku
build.gradle
modułu aplikacji, aby dodać podstawowe zależności AndroidX Test core i ext oraz zależność testową Robolectric.
app/build.gradle
// AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"
testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"
testImplementation "org.robolectric:robolectric:$robolectricVersion"
Krok 4. Dodawanie narzędzia JUnit Test Runner
- Dodaj
@RunWith(AndroidJUnit4::class)
nad klasą testową.
TasksViewModelTest.kt
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Test code
}
Krok 5. Korzystanie z AndroidX Test
W tym momencie możesz użyć biblioteki AndroidX Test. Obejmuje to metodę ApplicationProvider.getApplicationContex
t
, która pobiera kontekst aplikacji.
- Utwórz
TasksViewModel
za pomocąApplicationProvider.getApplicationContext()
z biblioteki testowej AndroidX.
TasksViewModelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
- Zadzwoń pod numer
addNewTask
, którego używatasksViewModel
.
TasksViewModelTest.kt
tasksViewModel.addNewTask()
Na tym etapie test powinien wyglądać jak poniższy kod.
TasksViewModelTest.kt
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
// TODO test LiveData
}
- Uruchom test, aby sprawdzić, czy działa.
Koncepcja: jak działa AndroidX Test?
Czym jest AndroidX Test?
AndroidX Test to zbiór bibliotek do testowania. Zawiera klasy i metody, które udostępniają wersje komponentów takich jak aplikacje i aktywności przeznaczone do testów. Na przykład ten napisany przez Ciebie kod jest przykładem funkcji AndroidX Test do uzyskiwania kontekstu aplikacji.
ApplicationProvider.getApplicationContext()
Jedną z zalet interfejsów API AndroidX Test jest to, że działają zarówno w przypadku testów lokalnych, jak i testów z instrumentacją. Jest to korzystne, ponieważ:
- Ten sam test możesz przeprowadzić jako test lokalny lub test z instrumentacją.
- Nie musisz poznawać różnych interfejsów API do testowania w przypadku testów lokalnych i instrumentowanych.
Na przykład jeśli kod został napisany przy użyciu bibliotek AndroidX Test, możesz przenieść klasę TasksViewModelTest
z folderu test
do folderu androidTest
, a testy nadal będą działać. getApplicationContext()
działa nieco inaczej w zależności od tego, czy jest uruchamiany jako test lokalny czy test z instrumentacją:
- Jeśli jest to test z instrumentacją, otrzyma on rzeczywisty kontekst aplikacji dostarczony podczas uruchamiania emulatora lub łączenia się z rzeczywistym urządzeniem.
- Jeśli jest to test lokalny, używa symulowanego środowiska Androida.
Co to jest Robolectric?
Symulowane środowisko Androida, którego AndroidX Test używa do testów lokalnych, jest udostępniane przez Robolectric. Robolectric to biblioteka, która tworzy symulowane środowisko Androida na potrzeby testów i działa szybciej niż uruchamianie emulatora lub testowanie na urządzeniu. Bez zależności Robolectric pojawi się ten błąd:
Do czego służy @RunWith(AndroidJUnit4::class)
?
Uruchamiający test to komponent JUnit, który uruchamia testy. Bez narzędzia do uruchamiania testów nie można ich przeprowadzić. JUnit udostępnia domyślny program do uruchamiania testów, który otrzymujesz automatycznie. @RunWith
zastępuje domyślny program do uruchamiania testów.
Program uruchamiający testy AndroidJUnit4
umożliwia uruchamianie testów AndroidX w różny sposób w zależności od tego, czy są to testy lokalne czy testy z instrumentacją.
Krok 6. Rozwiązywanie problemów z ostrzeżeniami Robolectric
Po uruchomieniu kodu zobaczysz, że używany jest Robolectric.
Dzięki AndroidX Test i programowi uruchamiającemu testy AndroidJunit4 nie musisz pisać ani jednej linijki kodu Robolectric.
Możesz zauważyć 2 ostrzeżenia.
No such manifest file: ./AndroidManifest.xml
"WARN: Android SDK 29 requires Java 9..."
Możesz naprawić ostrzeżenie No such manifest file: ./AndroidManifest.xml
, aktualizując plik Gradle.
- Dodaj do pliku Gradle ten wiersz, aby używany był prawidłowy plik manifestu Androida. Opcja includeAndroidResources umożliwia dostęp do zasobów Androida w testach jednostkowych, w tym do pliku AndroidManifest.
app/build.gradle
// Always show the result of every unit test when running via command line, even if it passes.
testOptions.unitTests {
includeAndroidResources = true
// ...
}
Ostrzeżenie "WARN: Android SDK 29 requires Java 9..."
jest bardziej skomplikowane. Przeprowadzanie testów na Androidzie Q wymaga Javy 9. Zamiast konfigurować Android Studio do używania Javy 9, w tym laboratorium zachowaj docelowy i kompilowany pakiet SDK na poziomie 28.
Podsumowanie:
- Testy czystych modeli widoku zwykle można umieszczać w zestawie źródeł
test
, ponieważ ich kod nie wymaga zwykle Androida. - Możesz użyć biblioteki testowej AndroidX, aby uzyskać testowe wersje komponentów takich jak aplikacje i aktywności.
- Jeśli musisz uruchomić symulowany kod Androida w
test
źródłowym, możesz dodać zależność Robolectric i adnotację@RunWith(AndroidJUnit4::class)
.
Gratulacje. Do uruchomienia testu używasz zarówno biblioteki testowej AndroidX, jak i Robolectric. Test nie został ukończony (nie napisano jeszcze instrukcji assert, jest tylko // TODO test LiveData
). Instrukcji assert nauczysz się w LiveData
.
Z tego zadania dowiesz się, jak prawidłowo potwierdzać wartość LiveData
.
Oto miejsce, w którym przerwaliśmy słuchanie audiobooka bez addNewTask_setsNewTaskEvent
testu modelu.
TasksViewModelTest.kt
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
// TODO test LiveData
}
Aby przetestować LiveData
, zalecamy wykonanie 2 czynności:
- Używaj klawisza
InstantTaskExecutorRule
- Sprawdź, czy
LiveData
obserwacja
Krok 1. Używanie reguły InstantTaskExecutorRule
InstantTaskExecutorRule
to reguła JUnit. Gdy użyjesz go z adnotacją @get:Rule
, spowoduje to uruchomienie części kodu w klasie InstantTaskExecutorRule
przed testami i po nich (aby zobaczyć dokładny kod, możesz użyć skrótu klawiszowego Command+B, aby wyświetlić plik).
Ta reguła uruchamia wszystkie zadania w tle związane z komponentami architektury w tym samym wątku, dzięki czemu wyniki testów są synchronizowane i pojawiają się w powtarzalnej kolejności. Jeśli piszesz testy, które obejmują testowanie LiveData, użyj tej reguły.
- Dodaj zależność Gradle dla podstawowej biblioteki testowej komponentów architektury (która zawiera tę regułę).
app/build.gradle
testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
- Otwórz aplikację
TasksViewModelTest.kt
- Dodaj
InstantTaskExecutorRule
do klasyTasksViewModelTest
.
TasksViewModelTest.kt
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
// Other code...
}
Krok 2. Dodawanie klasy LiveDataTestUtil.kt
Następnym krokiem jest upewnienie się, że testowany LiveData
jest obserwowany.
Gdy używasz LiveData
, aktywność lub fragment (LifecycleOwner
) zwykle obserwuje LiveData
.
viewModel.resultLiveData.observe(fragment, Observer {
// Observer code here
})
Ta obserwacja jest ważna. AbyLiveData
- wywoływać zdarzenia
onChanged
. - wywoływać przekształcenia.
Aby uzyskać oczekiwane działanie LiveData
w przypadku LiveData
modelu widoku, musisz obserwować LiveData
za pomocą LifecycleOwner
.
Stanowi to problem: w TasksViewModel
nie masz aktywności ani fragmentu, aby obserwować LiveData
. Aby to obejść, możesz użyć metody observeForever
, która zapewnia ciągłą obserwację LiveData
bez konieczności używania LifecycleOwner
. Gdy observeForever
, musisz pamiętać o usunięciu obserwatora, aby uniknąć wycieku informacji.
Wygląda to mniej więcej tak: Sprawdź:
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// Create observer - no need for it to do anything!
val observer = Observer<Event<Unit>> {}
try {
// Observe the LiveData forever
tasksViewModel.newTaskEvent.observeForever(observer)
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.value
assertThat(value?.getContentIfNotHandled(), (not(nullValue())))
} finally {
// Whatever happens, don't forget to remove the observer!
tasksViewModel.newTaskEvent.removeObserver(observer)
}
}
To dużo kodu szablonowego, aby zaobserwować pojedynczy element LiveData
w teście. Istnieje kilka sposobów, aby się go pozbyć. Utworzysz funkcję rozszerzającą o nazwie LiveDataTestUtil
, aby uprościć dodawanie obserwatorów.
- W zbiorze źródeł
test
utwórz nowy plik Kotlin o nazwieLiveDataTestUtil.kt
.
- Skopiuj i wklej poniższy kod.
LiveDataTestUtil.kt
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
@Suppress("UNCHECKED_CAST")
return data as T
}
Jest to dość skomplikowana metoda. Tworzy funkcję rozszerzającą Kotlin o nazwie getOrAwaitValue
, która dodaje obserwatora, pobiera wartość LiveData
, a następnie usuwa obserwatora. Jest to w zasadzie krótka wersja kodu observeForever
pokazanego powyżej, którą można ponownie wykorzystać. Pełne wyjaśnienie tej klasy znajdziesz w tym poście na blogu.
Krok 3. Użyj getOrAwaitValue, aby napisać asercję
W tym kroku użyjesz metody getOrAwaitValue
i napiszesz instrukcję assert, która sprawdzi, czy wywołano funkcję newTaskEvent
.
- Uzyskaj wartość
LiveData
dlanewTaskEvent
za pomocągetOrAwaitValue
.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
- Sprawdź, czy wartość nie jest pusta.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))
Kompletny test powinien wyglądać tak, jak pokazano poniżej.
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.nullValue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
assertThat(value.getContentIfNotHandled(), not(nullValue()))
}
}
- Uruchom kod i sprawdź, czy test został zaliczony.
Teraz, gdy wiesz już, jak napisać test, zrób to samodzielnie. W tym kroku, korzystając z nabytej wiedzy, przećwicz pisanie kolejnego testu TasksViewModel
.
Krok 1. Pisanie własnego testu klasy ViewModel
Napiszesz setFilterAllTasks_tasksAddViewVisible()
. Ten test powinien sprawdzać, czy jeśli typ filtra został ustawiony na wyświetlanie wszystkich zadań, przycisk Dodaj zadanie jest widoczny.
- Korzystając z
addNewTask_setsNewTaskEvent()
jako odniesienia, napisz test wTasksViewModelTest
o nazwiesetFilterAllTasks_tasksAddViewVisible()
, który ustawia tryb filtrowania naALL_TASKS
i sprawdza, czytasksAddViewVisible
LiveData ma wartośćtrue
.
Aby rozpocząć, użyj kodu poniżej.
TasksViewModelTest
@Test
fun setFilterAllTasks_tasksAddViewVisible() {
// Given a fresh ViewModel
// When the filter type is ALL_TASKS
// Then the "Add task" action is visible
}
Uwaga:
- Wartość wyliczenia
TasksFilterType
dla wszystkich zadań toALL_TASKS.
. - Widoczność przycisku dodawania zadania jest kontrolowana przez ustawienie
LiveData
tasksAddViewVisible.
.
- Przeprowadź test.
Krok 2. Porównanie testu z rozwiązaniem
Porównaj swoje rozwiązanie z poniższym.
TasksViewModelTest
@Test
fun setFilterAllTasks_tasksAddViewVisible() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When the filter type is ALL_TASKS
tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)
// Then the "Add task" action is visible
assertThat(tasksViewModel.tasksAddViewVisible.getOrAwaitValue(), `is`(true))
}
Sprawdź, czy:
tasksViewModel
tworzysz za pomocą tego samego wyrażenia AndroidXApplicationProvider.getApplicationContext()
.- Wywołujesz metodę
setFiltering
, przekazując wyliczenie typu filtraALL_TASKS
. - Sprawdź, czy wartość
tasksAddViewVisible
to „true” (prawda), używając metodygetOrAwaitNextValue
.
Krok 3. Dodawanie reguły @Before
Zwróć uwagę, że na początku obu testów definiujesz TasksViewModel
.
TasksViewModelTest
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
Jeśli masz powtarzający się kod konfiguracji w przypadku wielu testów, możesz użyć adnotacji @Before, aby utworzyć metodę konfiguracji i usunąć powtarzający się kod. Wszystkie te testy będą testować TasksViewModel
i wymagają modelu widoku, więc przenieś ten kod do bloku TasksViewModel
.@Before
- Utwórz zmienną instancji
lateinit
o nazwietasksViewModel|
. - Utwórz metodę o nazwie
setupViewModel
. - Dodaj do niego adnotację za pomocą
@Before
. - Przenieś kod tworzenia instancji modelu widoku do
setupViewModel
.
TasksViewModelTest
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
- Uruchom kod.
Ostrzeżenie
Nie wykonuj tych czynności, nie inicjuj
tasksViewModel
z definicją:
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
Spowoduje to użycie tej samej instancji do wszystkich testów. Należy tego unikać, ponieważ każdy test powinien mieć nową instancję testowanego obiektu (w tym przypadku ViewModel).
Ostateczny kod dla TasksViewModelTest
powinien wyglądać tak, jak pokazano poniżej.
TasksViewModelTest
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
// Executes each task synchronously using Architecture Components.
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
@Test
fun addNewTask_setsNewTaskEvent() {
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.awaitNextValue()
assertThat(
value?.getContentIfNotHandled(), (not(nullValue()))
)
}
@Test
fun getTasksAddViewVisible() {
// When the filter type is ALL_TASKS
tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)
// Then the "Add task" action is visible
assertThat(tasksViewModel.tasksAddViewVisible.awaitNextValue(), `is`(true))
}
}
Kliknij tutaj, aby zobaczyć różnicę między kodem początkowym a końcowym.
Aby pobrać kod ukończonego ćwiczenia, możesz użyć tego polecenia git:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout end_codelab_1
Możesz też pobrać repozytorium jako plik ZIP, rozpakować go i otworzyć w Android Studio.
W tym ćwiczeniu z programowania omówiliśmy:
- Jak uruchamiać testy w Androidzie Studio.
- Różnica między testami lokalnymi (
test
) a testami z użyciem instrumentacji (androidTest
). - Jak pisać lokalne testy jednostkowe za pomocą JUnit i Hamcrest.
- Konfigurowanie testów ViewModel za pomocą biblioteki testowej AndroidX.
Kurs Udacity:
Dokumentacja dla deweloperów aplikacji na Androida:
- Przewodnik po architekturze aplikacji
- JUnit4
- Hamcrest
- Biblioteka testów Robolectric
- Biblioteka testowa AndroidX
- Biblioteka testowa AndroidX Architecture Components Core
- zbiory źródeł,
- Testowanie z wiersza poleceń
Materiały wideo:
Inne:
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z zaawansowanego Androida w Kotlinie.