Testy jednostkowe z JUnit
Pisz szybkie i niezawodne testy jednostkowe za pomocą JUnit 4. Używaj @Test, @Before, @After i asercji, testuj LiveData z InstantTaskExecutorRule oraz korutyny z runTest.
Testy jednostkowe z JUnit to bezpłatna lekcja Android Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Android Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Android Academy zawiera 4 lekcji w sumie.
Po co pisać testy?
Testy nie są luksusem — stanowią siatkę bezpieczeństwa:
- Wykrywają błędy, zanim zrobią to użytkownicy
- Pozwalają bez obaw refaktoryzować kod (jeśli testy przechodzą, niczego nie zepsuto)
- Dokumentują zamierzone działanie
- Zapobiegają regresjom podczas dodawania funkcji
Dobrze przetestowany kod jest również zwykle lepiej zaprojektowany — jeśli trudno go testować, projekt prawdopodobnie jest zbyt silnie powiązany.
Rodzaje testów Android
Dwie kategorie:
- Testy jednostkowe (folder
test/) — uruchamiane na JVM, bez potrzeby używania urządzenia. Szybkie. Testują zwykłą logikę Kotlin/Java. - Testy instrumentacyjne (folder
androidTest/) — uruchamiane na urządzeniu lub emulatorze. Wolniejsze. Testują kod zależny od Androida (bazę Room, interfejs użytkownika).
Preferuj testy jednostkowe dla logiki biznesowej. Testów instrumentacyjnych używaj tylko wtedy, gdy musisz korzystać z API Androida.
Konfiguracja JUnit 4
JUnit 4 jest domyślnie dołączony do projektów Android. Dodaj te zależności do app/build.gradle:
// app/build.gradle:
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
testImplementation 'androidx.arch.core:core-testing:2.2.0' // for LiveData
}Pierwszy test JUnit
Utwórz klasę w src/test/java/ i oznaczaj metody testowe adnotacją @Test:
import org.junit.Test
import org.junit.Assert.*
class CalculatorTest {
private val calc = Calculator()
@Test
fun `addition returns correct sum`() {
val result = calc.add(2, 3)
assertEquals(5, result)
}
@Test
fun `division by zero throws exception`() {
assertThrows(ArithmeticException::class.java) {
calc.divide(10, 0)
}
}
}@Before i @After
Używaj adnotacji cyklu życia, aby przygotowywać i sprzątać stan przed każdym testem i po nim:
class UserRepositoryTest {
private lateinit var repo: UserRepository
private lateinit var fakeDao: FakeUserDao
@Before
fun setUp() {
fakeDao = FakeUserDao() // fresh instance before each test
repo = UserRepository(fakeDao)
}
@After
fun tearDown() {
fakeDao.clear() // clean up after each test
}
@Test
fun `getUsers returns all users`() {
fakeDao.insert(User(1, "Alice"))
fakeDao.insert(User(2, "Bob"))
assertEquals(2, repo.getUsers().size)
}
}Typowe asercje
JUnit udostępnia metody asercji w Assert.*:
// Equality:
assertEquals(expected, actual)
assertNotEquals(unexpected, actual)
// Null:
assertNull(value)
assertNotNull(value)
// Boolean:
assertTrue(condition)
assertFalse(condition)
// Same object reference:
assertSame(expected, actual)
// Custom message on failure:
assertEquals("User ID should be 1", 1, user.id)
// Exception:
assertThrows(IllegalArgumentException::class.java) {
User(id = -1, name = "")
}Testowanie ViewModel
Aby testować LiveData, dodaj InstantTaskExecutorRule, dzięki czemu LiveData będzie publikować dane synchronicznie:
class UserViewModelTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val fakeRepo = FakeUserRepository()
private lateinit var viewModel: UserViewModel
@Before
fun setUp() {
viewModel = UserViewModel(fakeRepo)
}
@Test
fun `users are loaded on init`() {
fakeRepo.usersToReturn = listOf(User(1, "Alice"), User(2, "Bob"))
viewModel.loadUsers()
val result = viewModel.users.value
assertNotNull(result)
assertEquals(2, result?.size)
}
}Testowanie korutyn
Używaj TestCoroutineDispatcher / StandardTestDispatcher, aby kontrolować wykonywanie korutyn w testach:
class SyncWorkerTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val testDispatcher = StandardTestDispatcher()
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `sync completes successfully`() = runTest {
val repo = FakeRepository()
val worker = SyncUseCase(repo, testDispatcher)
val result = worker.run()
assertTrue(result.isSuccess)
}
}Fake a Mock
Dwie strategie zastępowania rzeczywistych zależności w testach:
- Fake — działająca implementacja zaprojektowana do testowania (np. lista przechowywana w pamięci zamiast prawdziwej bazy danych). Łatwa do napisania, czytelna i niewymagająca biblioteki.
- Mock — automatycznie generowany obiekt zastępczy, który rejestruje wywołania i pozwala weryfikować interakcje (Mockito, MockK). Potężniejszy, ale przy nadużywaniu może prowadzić do kruchych testów.
Preferuj obiekty Fake w prostych przypadkach; używaj obiektów Mock, gdy trzeba weryfikować wywołania metod.
Pisanie obiektu Fake
Obiekt Fake implementuje ten sam interfejs co prawdziwa klasa, ale korzysta z przechowywania danych w pamięci:
interface UserDao {
fun getAll(): List<User>
fun insert(user: User)
}
class FakeUserDao : UserDao {
private val storage = mutableListOf<User>()
override fun getAll(): List<User> = storage.toList()
override fun insert(user: User) { storage.add(user) }
fun clear() { storage.clear() }
}Konwencja nazewnictwa testów
Dobre nazwy testów opisują, co jest testowane, oraz oczekiwany rezultat. W Kotlinie używaj nazw funkcji w backtickach, aby opisy były czytelne:
`getUsers returns empty list when no users exist``login throws exception when password is blank``add returns correct sum for negative numbers`
Wzorzec: `subject does X when Y`
Szybki test
Jaki jest cel adnotacji @Before w klasie testowej JUnit?
Podsumowanie: testy jednostkowe z JUnit
Niezawodne aplikacje to aplikacje przetestowane:
- Testy jednostkowe umieszczaj w
src/test/; są uruchamiane na JVM — szybko - Oznaczaj je adnotacją
@Test, używajassertEquals,assertTrue,assertThrows @Before/@Aftersłużą do przygotowania i sprzątania stanuInstantTaskExecutorRulesłuży do synchronicznego testowania LiveDatarunTest+StandardTestDispatchersłużą do testowania korutyn- W przypadku prostych zamienników preferuj obiekty Fake zamiast Mock
Następnie: mockowanie z Mockito w celu weryfikowania interakcji.
Często zadawane pytania
Czy lekcja „Testy jednostkowe z JUnit” jest bezpłatna?
Tak — pełny tekst „Testy jednostkowe z JUnit” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Android Academy, przejdź na CoddyKit PRO. Kurs Android Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Testy jednostkowe z JUnit”?
Pisz szybkie i niezawodne testy jednostkowe za pomocą JUnit 4. Używaj @Test, @Before, @After i asercji, testuj LiveData z InstantTaskExecutorRule oraz korutyny z runTest. Ćwiczysz Android Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Android Academy?
Nie wymagamy żadnego doświadczenia. Android Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Testy jednostkowe z JUnit”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Android Academy?
Tak. Każda lekcja Android Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Testy jednostkowe z JUnit
- Mockowanie za pomocą Mockito
- Testowanie interfejsu z Espresso
- Debugowanie i profilowanie