Test Doubles — jsou náhradní objekty používané v unit testech místo skutečných závislostí. Termín zavedl Gerard Meszaros v knize „xUnit Test Patterns“ (2007) jako zastřešující pojem pro Mock, Stub, Fake, Spy a Dummy. Podle Martin Fowler (2024), Test Doubles umožňují izolovat testovaný komponent od jeho prostředí, čímž jsou testy deterministické, rychlé a nezávislé na externích službách.
Hlavní
Test Doubles — je termín z automobilového průmyslu (kaskadér, „dvojník“ pro herce), přenesený do vývoje softwaru. Jako kaskadér nahrazuje herce v nebezpečné scéně, Test Double nahrazuje skutečný komponent v testovacím scénáři. To je nutné, když skutečná závislost není dostupná, je pomalá, nedeterministická nebo má vedlejší účinky.
Koncepce Test Double zahrnuje pět konkrétních typů, z nichž každý řeší svůj vlastní úkol. Meszarosova typologie je kanonická a používá se ve všech moderních průvodcích testováním. Rozdíl mezi typy je ve stupni kontroly a ověření: od jednoduchého vyplnění parametrů (Dummy) až po úplnou kontrolu posloupnosti volání (Mock).
Hlavním cílem Test Doubles je izolace testovaného modulu. V mobilním vývoji jsou skutečnými závislostmi API servery, databáze, souborový systém, senzory zařízení, systémové služby (LocationManager, Camera, Bluetooth). Přímé použití těchto komponentů činí testy pomalými, křehkými a závislými na prostředí. Podle Google Testing Blog (2023), dobře izolované unit testy běží v milisekundách, zatímco integrační testy v sekundách a minutách.
Klasifikace Gerard Meszaros zahrnuje pět typů Test Doubles, které se liší chováním a účelem použití. Porozumění rozdílům mezi nimi je základem správného unit testování.
Dummy — je objekt předaný testované metodě, který ale nikdy není použit. Dummy je potřebný jen pro splnění signatury metody. V Kotlin je to často null, emptyList() nebo objekt s náhradami. Dummy by neměl obsahovat žádnou logiku — pokud je zavolán, test by měl selhat.
Fake — je zjednodušená, ale funkční implementace rozhraní. Na rozdíl od Mock a Stub, Fake obsahuje skutečnou business logiku, ale ve zjednodušené formě. Klasický příklad — InMemoryUserRepository, který ukládá data v HashMap místo databáze. Fake se používá, když je třeba testovat logiku závislou na stavu, ale bez režie skutečné infrastruktury.
| Typ | Účel | Příklad |
|---|---|---|
| Dummy | Vyplnit parametr | null, prázdný objekt |
| Fake | Funkční zjednodušená implementace | InMemoryRepository |
| Stub | Vrátit pevnou hodnotu | when(api.getUser()).thenReturn(user) |
| Spy | Zaznamenat volání pro kontrolu | verify(spy).save(user) |
| Mock | Zkontrolovat interakci | verify(mock).sendEmail(email) |
Stub vrací předem dané hodnoty na konkrétní volání. Stub nekontroluje, zda byl zavolán — jednoduše poskytuje data. V Mockito se Stub vytváří přes when(method).thenReturn(value). Stub je ideální pro testování, když závislost musí vrátit konkrétní hodnotu, ale samotný fakt volání není důležitý.
Spy — je obal kolem skutečného objektu, který zaznamenává všechna volání pro pozdější ověření. Na rozdíl od Mock, Spy deleguje volání na skutečný objekt, ale umožňuje ověřit, že k nim došlo. V Mockito se Spy vytváří přes spy(realObject). Spy je užitečný pro částečné mockování, když chcete použít skutečný objekt, ale ověřit některá volání.
Mock — je objekt s předdefinovanými očekáváními volání. Mock kontroluje, zda byly určité metody zavolány s určitými argumenty a v určitém pořadí. Na rozdíl od Stub, Mock se zaměřuje na ověření chování, nikoli na vracení dat. Mock je nejsilnější a nejčastěji používaný typ Test Double v mobilním vývoji.
Rozdíl mezi Mock a Stub často způsobuje zmatek i mezi zkušenými vývojáři. Hlavní rozdíl je v účelu: Stub kontroluje stav (state verification), Mock kontroluje chování (behavior verification).
Stub odpovídá na otázku: „vrátil kód správný výsledek?“. Mock odpovídá na otázku: „zavolal kód správné metody se správnými argumenty?“. V mobilním vývoji se Stub používá, když je důležitý výsledek (např. data z repozitáře), a Mock, když jsou důležité vedlejší účinky (např. odeslání emailu, zápis do databáze).
// Stub: kontrola stavu
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)
// Mock: kontrola chování
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }
Praktické příklady všech pěti typů Test Doubles v Kotlin s použitím MockK — nejpopulárnější knihovny mocking pro Android projekty.
class InMemoryUserRepository : UserRepository {
private val store = mutableMapOf<String, User>()
override fun save(user: User) {
store[user.email] = user
}
override fun findByEmail(email: String): User? {
return store[email]
}
}
class RegisterUseCaseTest {
private val api = mockk<AuthApi>()
private val repo = spyk(InMemoryUserRepository())
private val useCase = RegisterUseCase(api, repo)
fun `register user successfully`() = runTest {
// Stub: vrácení pevné odpovědi API
coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")
val result = useCase.execute("test@test.com")
// Verify: kontrola, že uživatel byl uložen
verify { repo.save(any()) }
assertTrue(result.isSuccess())
}
}
data class Logger(val appContext: Context, val format: FormatType)
fun `test logger with dummy context`() {
// Dummy: Context není uvnitř Logger použit
val dummyContext = mockk<Context>()
val logger = Logger(dummyContext, FormatType.JSON)
assertEquals(FormatType.JSON, logger.format)
}
Výběr typu Test Double závisí na tom, co přesně se testuje: stav, chování nebo integrace. V mobilním vývoji na Android a iOS se vytvořila následující doporučení.
Při testování ViewModel použijte Mock pro závislosti, které způsobují vedlejší účinky (repozitáře, analytika, navigace), a Stub pro závislosti, které vracejí data (API klienti, ContentProvider). To umožňuje ověřit, že Viewmodel správně zpracovává úspěšné i chybové scénáře.
Na úrovni Repository jsou preferovány Fake (implementace databáze in-memory) a Stub (pevné odpovědi API). Fake umožňuje testovat logiku cache a offline režimu bez konfigurace SQLite. Stub simuluje různé HTTP statusy: 200, 404, 500, timeout.
Nesprávné použití Test Doubles — jeden z nejčastějších důvodů křehkých testů, které se při každém refaktorování rozbijí.
Nejčastější chyba — mockování všeho. Pokud je každá závislost v testu nahrazena Mockem, test přestává kontrolovat skutečné chování. Mock by měl být pouze pro externí závislosti (síť, databáze, souborový systém, systémové služby). Vnitřní komponenty aplikace (Value Object, data class, jednoduché utility) by neměly být nahrazovány.
Druhá chyba — vytvoření Mock bez definice očekávání. Pokud je metoda zavolána bez every / when, Mock vrátí výchozí hodnotu (null, 0, false). To může vést k falešně pozitivním testům, když Mock tiše vrátí null a test to interpretuje jako správné chování.
Třetí chyba — kontrola každého volání každého Mocku. Verify by měl být použit pouze pro volání kritická z hlediska business logiky. Nadměrné ověřování činí testy křehkými: změna pořadí volání v produkčním kódu rozbíjí testy bez změny chování.
Často kladené otázky
Stub vrací data a kontroluje stav (co bylo vráceno), zatímco Mock kontroluje chování (jaké metody byly zavolány). Stub = „vrať X“, Mock = „zkontroluj, že Y bylo zavoláno s argumentem Z“. V reálných testech jeden objekt často slouží zároveň jako Stub i Mock.
Fake je preferován před Mockem, když se testuje logika závislá na stavu: cache, offline režim, transakce. Fake (in-memory implementace) umožňuje testovat tyto scénáře bez křehkých verify volání. Mock je vhodnější pro kontrolu odesílání dat: analytika, push notifikace, email.
Pro Android projekty v Kotlin se doporučuje MockK. Podporuje korutiny, suspend funkce, sealed class a extension funkce bez další konfigurace. Pro Java projekty zůstává standardem Mockito — nejpopulárnější knihovna s rozsáhlou dokumentací.
Pro testování Kotlin Flow použijte knihovnu Turbine spolu s MockK. Turbine zjednodušuje kontrolu emise Flow: lze zkontrolovat pořadí hodnot, dokončení toku a výjimky. Stub pro Flow vrací flowOf(value), Mock kontroluje, že Flow byl shromážděn.
Ano, ale na úrovni API odpovědí, nikoli UI komponent. Knihovny MockWebServer (OkHttp) a WireMock umožňují nahrazovat HTTP odpovědi v UI testech. Samotné UI komponenty (Compose, SwiftUI Views) by neměly být nahrazovány — jejich chování se testuje pomocí screenshot testů a Espressa.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také