Stub (náhrada, atrapa) — testovací objekt, který vrací předdefinované odpovědi na volání metod namísto skutečné implementace. V mobilním vývoji stuby izolují testovaný modul od síťových požadavků, databáze a souborového systému, což umožňuje kontrolu logiky bez nastavování prostředí. Na rozdíl od mock, stub neověřuje chování — pouze poskytuje data. Více v článku Martina Fowlera o test doubles.
Hlavní body
Stub — je náhradní objekt, který nahrazuje skutečnou závislost v testu a vrací předem stanovené hodnoty na konkrétní volání. Termín byl zaveden v klasifikaci Gerarda Meszarose (2007) v knize „xUnit Test Patterns". Stub patří do kategorie test doubles — objektů nahrazujících skutečné komponenty během testování. Hlavním účelem stubu je poskytnout testovanému bloku předvídatelná data a odstranit nejistotu externích systémů.
Princip fungování — test nakonfiguruje stub před provedením: „až bude metoda getUsers() zavolána, vrať tento seznam uživatelů". Stub neobsahuje obchodní logiku, nekontroluje pořadí volání a nezaznamenává historii přístupů. Prostě stojí na místě skutečné komponenty a vrací to, co mu bylo řečeno. V kontextu testování Androidu to znamená, že OkHttp klient neposílá skutečný požadavek na server, ale dostává odpověď z MockWebServer nakonfigurovaného jako stub.
Kdy použít — stuby jsou optimální pro testování UI vrstvy (ViewModel, Presenter) a obchodní logiky (UseCase, Interactor), kde je třeba zkontrolovat reakci na konkrétní data: prázdný seznam, server vrátil chybu 500, vypršel token. Každý případ, kdy test vyžaduje konkrétní vstupní stav, je úkolem pro stub. Pro každý testovací scénář se vytváří vlastní konfigurace stubu, což činí testy čitelné a předvídatelné.
Gerard Meszaros (2007) v knize „xUnit Test Patterns" identifikoval pět typů test doubles: dummy, stub, spy, mock, fake. Každý typ řeší svůj vlastní úkol. Dummy — je předán, ale nepoužívá se. Stub — vrací data. Spy — zaznamenává volání. Mock — ověřuje chování. Fake — obsahuje zjednodušenou logiku. Pochopení této klasifikace pomáhá vývojáři vybrat správný nástroj pro každý testovací scénář.
Síťové požadavky — nejčastější scénář použití stubů. Aplikace provádí HTTP volání na API a v testu je třeba zkontrolovat reakci na různé odpovědi: úspěšný JSON, chyba 401 (neautorizováno), timeout, prázdné pole. MockWebServer (OkHttp) na Androidu a URLProtocol (iOS) fungují jako stuby a vracejí předdefinované HTTP odpovědi bez skutečného připojení k serveru. To zrychluje testy z vteřin na milisekundy.
Databáze — Room (Android) a CoreData (iOS) mají in-memory varianty, ale jejich nastavení stále vyžaduje čas. Stub místo repozitáře vrací předem připravené seznamy Entity, aniž by se dotýkal databáze. To je zvláště účinné pro testování ViewModel, kde je třeba zkontrolovat řazení, filtrování nebo transformaci dat. Test se provádí v milisekundách bez ohledu na objem dat.
Systémové služby — LocationManager, SensorManager, SharedPreferences vyžadují skutečné zařízení nebo emulátor. Stub pro LocationProvider vrací zadané souřadnice, pro SensorManager — pevné hodnoty akcelerometru. Na iOS je analogem CLLocationManager s testovací implementací delegáta. Bez stubů takové testy vyžadují fyzické zařízení s konkrétními podmínkami.
Souborový systém a cache — načítání obrázků, ukládání odpovědí do mezipaměti, práce s konfiguračními soubory — všechny tyto operace závisí na stavu disku. Stub pro FileManager nebo ImageCache vrací úspěch/chybu bez čtení skutečných souborů. To eliminuje falešná selhání testů kvůli neshodě cest nebo oprávnění na různých vývojářských počítačích.
Rozdělení odpovědnosti — tři typy test doubles řeší různé úkoly. Stub: „dej mi data". Mock: „zkontroluj, zda jsem byl zavolán". Fake: „funguji jako skutečný, jen jednodušeji". Rozdíl je kritický pro čitelnost testů: pokud test používá mock tam, kde je potřeba stub, je přetížen voláními verify, která nesouvisejí s testovaným scénářem.
| Vlastnost | Stub | Mock | Fake |
|---|---|---|---|
| Účel | Poskytnout data | Ověřit interakci | Zjednodušená implementace |
| Logika | Ne | Ne | Ano (ale zjednodušená) |
| Ověření | Ne | Ano (verify) | Nepřímé (přes stav) |
| Flexibilita | Nízká — pevné odpovědi | Střední | Vysoká — logika se přizpůsobuje |
| Rychlost | Maximální | Vysoká | Střední |
| Příklad | MockWebServer vrací JSON | Mockito.verify(repository).save() | InMemoryRepository s HashMap |
Praktické pravidlo — pokud test kontroluje, jaká data testovaná komponenta obdržela — použijte stub. Pokud test kontroluje, zda komponenta zavolala metodu závislosti se správnými argumenty — použijte mock. Pokud chcete jednoduše nahradit databázi hash tabulkou — to je fake. Míchání typů v jednom testu ho činí křehkým: při změně implementace bude třeba přepsat jak stub, tak verify logiku.
Stub s verify — častá chyba, když vývojář nakonfiguruje stub a poté přidá verify(stub).method(). Stub by podle definice neměl být ověřován — pro ověření existuje mock. Pokud potřebujete zkontrolovat, že metoda byla zavolána s konkrétními argumenty, použijte Mockito.mock() místo Mockito.stub(). Toto oddělení udržuje záměr testu jasný pro ostatní vývojáře.
MockWebServer — OkHttp knihovna pro vytváření HTTP stubů na Androidu a JVM. Spouští lokální HTTP server na určeném portu, který zachycuje požadavky OkHttp klienta a vrací předdefinované odpovědi. Nastavení trvá tři řádky: vytvořit server, zařadit odpověď do fronty (enqueue), spustit. Test může postupně zařadit do fronty několik odpovědí pro scénáře se stránkováním nebo opakováním.
class UserRepositoryTest {
private val server = MockWebServer()
fun setup() {
server.start(8080)
val client = OkHttpClient.Builder()
.readTimeout(1, TimeUnit.SECONDS)
.build()
}
fun test_user_list_success() {
val json = "[{\"id\":1,\"name\":\"Alice\"}]"
server.enqueue(MockResponse()
.setBody(json)
.setResponseCode(200)
)
val result = repository.getUsers()
assertEquals(1, result.size)
}
fun teardown() {
server.shutdown()
}
}
MockK — alternativa k Mockito pro Kotlin s first-class podporou pro korutiny, rozšiřující funkce a sealed třídy. Stuby v MockK se vytvářejí pomocí coEvery (pro suspend funkce) a every (pro běžné funkce). Na rozdíl od MockWebServer, MockK nahrazuje jednotlivé metody závislostí, nikoli celou HTTP vrstvu. To je vhodné pro unit testy UseCase nebo Interactor, kde jsou závislostmi abstrakce repozitářů.
interface UserRepository {
suspend fun getUsers(): List<User>
}
class GetUsersUseCaseTest {
private val repo = mockk<UserRepository>()
private val useCase = GetUsersUseCase(repo)
fun test_empty_list() = runTest {
coEvery { repo.getUsers() } returns emptyList()
val result = useCase.invoke()
assertTrue(result.isEmpty())
coVerify(exactly = 1) { repo.getUsers() }
}
}
Best practice — pro integrační testy používejte MockWebServer (zachycuje skutečný HTTP), pro unit testy — MockK (nahrazuje rozhraní). Nenahrazujte to, co netestujete: pokud test kontroluje Repository, nenahrazujte v něm OkHttp klienta — použijte skutečný MockWebServer na úrovni HTTP. Toto pravidlo udržuje testy relevantní a snižuje křehkost při refaktorování.
Swift protokoly jako stuby — v nativním přístupu iOS se stub implementuje vložením testovací struktury odpovídající protokolu závislosti. Místo skutečného NetworkService test obdrží StubNetworkService, který vrací pevná data. Swift je jazyk se statickým typováním, takže stub musí odpovídat stejnému protokolu jako skutečná služba. Kompilátor garantuje, že stub implementuje všechny požadované metody.
protocol NetworkServiceProtocol {
func fetchUsers() async throws -> [User]
}
struct StubNetworkService: NetworkServiceProtocol {
let result: Result<[User], Error>
func fetchUsers() async throws -> [User] {
try result.get()
}
}
final class UsersViewModelTests: XCTestCase {
func test_success_state() async {
let stub = StubNetworkService(
result: .success([User(name: "Alice")])
)
let vm = UsersViewModel(service: stub)
await vm.load()
XCTAssertEqual(vm.users.count, 1)
}
}
OCMock pro Objective-C — knihovna pro vytváření stubů a mocků v legacy iOS projektech. OCMock podporuje stub metody s argumenty a návratovými hodnotami. Moderní projekty ve Swiftu preferují přístup založený na protokolech s ručními stuby — to poskytuje kontrolu nad každou metodou a nevyžaduje externí závislosti. OCMock zůstává volbou pro projekty, kde protokolizace všech závislostí není ekonomicky proveditelná.
URLProtocol pro HTTP stuby — systémový mechanismus iOS pro zachycování síťových požadavků prostřednictvím podtřídy URLProtocol. Test registruje vlastní URLProtocol, který zachycuje URLSession a vrací stub odpovědi. Výhoda oproti ručním stubům: není třeba měnit architekturu aplikace — URLSession zůstává skutečný, ale data jsou nahrazována na úrovni protokolu. Nevýhoda: hůře se debuguje než explicitní stub služba.
Často kladené otázky
Stub vrací předdefinovaná data a nekontroluje skutečnost volání. Mock navíc ověřuje, že metoda byla zavolána se správnými argumenty (verify). Stub odpovídá na otázku „co vrátit", Mock — na otázku „bylo volání". Použijte stub pro kontrolu stavu, mock — pro kontrolu interakce.
Fake je potřeba, když test vyžaduje fungující (byť zjednodušenou) implementaci — například in-memory databázi místo Room. Stub je vhodný pro jednotlivé scénáře s předdefinovanými daty. Pokud opakujete stejný stub v 10 testech — pravděpodobně potřebujete Fake. Fake snižuje duplicitu, protože logika žije v jedné třídě.
Na Androidu — MockK pro Kotlin objekty (object) podporuje mockkObject(), včetně statických metod Java tříd prostřednictvím mockkStatic(). Na iOS — statické metody Swift nelze přímo stubovat; použijte protokoly a DI k nahrazení static volání instanční metodou protokolu. Statické stuby jsou technický dluh a je třeba se jim vyhýbat v novém kódu.
Použijte MockWebServer (OkHttp) — funguje jako lokální HTTP server, který zařazuje odpovědi do fronty (enqueue). Pro Retrofit stačí změnit základní URL na localhost:8080. Pro Ktor použijte MockEngine — vestavěný mechanismus pro nahrazení HttpStatement. Oba přístupy fungují bez skutečného internetu a poskytují plnou kontrolu nad stavovým kódem, tělem a hlavičkami odpovědi.
Spy obaluje skutečný objekt a zaznamenává volání, zatímco Stub zcela nahrazuje objekt pevnými odpověďmi. Spy umožňuje částečné použití skutečné implementace (ostatní metody fungují jako dříve), zatímco stub ne. Pokud potřebujete zkontrolovat, že metoda byla zavolána, ale část logiky se má provést — použijte spy, ne stub.
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é