Fake (fejk) — fungující zjednodušená implementace závislosti, která se chová jako skutečná komponenta, ale místo produkční infrastruktury používá in-memory úložiště nebo jiné lehké mechanismy. Na rozdíl od stub, fake obsahuje skutečnou obchodní logiku — řazení, filtrování, agregaci — prostě bez vnějších efektů. In-memory databáze místo Room nebo HashMap místo SharedPreferences — klasické příklady. Více v klasifikaci test doubles Martina Fowlera.
Hlavní body
Fake — úplná, ale lehká implementace rozhraní, vhodná pro testování. Termín zavedl Gerard Meszaros (2007) v knize «xUnit Test Patterns». Na rozdíl od stub, který vrací striktně předdefinované odpovědi, fake obsahuje spustitelný kód: může řadit seznam, filtrovat podle podmínky, počítat záznamy. Jediný rozdíl od produkční implementace — fake pracuje s in-memory daty a neprovádí skutečné I/O operace.
Hlavní výhoda — rychlost. Testy s fake se provádějí v milisekundách, protože není přístup k disku, síti nebo databázi. In-memory HashMap pracuje 100-1000 krát rychleji než Room nebo CoreData. Zároveň fake kontroluje skutečnou obchodní logiku: řazení, filtrování, agregaci — vše, co stub nemůže zkontrolovat, protože stub vrací jen to, co mu bylo řečeno. Fake dává jistotu, že kód správně zpracovává data, nejen přijímá předdefinovanou odpověď.
Fake je lepší než Stub — pokud testovaná komponenta provádí několik operací s daty (načetl, filtroval, seřadil, uložil), stub bude vyžadovat konfiguraci každého volání zvlášť. Fake obsahuje logiku uvnitř — test jednoduše volá metody a kontroluje výsledek. V IT Sectr používáme fake pro všechny repozitáře v unit testech: fake-repozitář s HashMap pokrývá 90% scénářů bez konfigurace Mockito nebo MockK.
Kritérium výběru — určete, co test kontroluje: stav nebo interakci. Pokud test kontroluje stav (výsledek práce) a používá logiku — je potřeba fake. Pokud test potřebuje pouze vstupní data bez logiky — stačí stub. Pokud test kontroluje samotné volání metody — je potřeba mock. Míchání typů test doubles v jednom testu ztěžuje porozumění a zvyšuje křehkost.
| Kritérium | Fake | Stub | Mock |
|---|---|---|---|
| Přítomnost logiky | Ano (zjednodušená) | Ne | Ne |
| Rychlost | Vysoká | Maximální | Vysoká |
| Kontrola chování | Nepřímá | Ne | Ano (verify) |
| Údržba | Jedna třída na rozhraní | Konfigurace pro každý test | Konfigurace pro každý test |
| Realismus | Vysoký (kód funguje) | Nízký (pevná data) | Střední |
| Riziko falešných poplachů | Nízké | Střední | Vysoké (křehké testy) |
Antivzor: Fake, který není fake — častá chyba, když vývojář nazývá fake objekt, který je ve skutečnosti stub nebo mock. Pokud váš InMemoryUserRepository neobsahuje logiku (filtrování, řazení) — není to fake, ale stub s in-memory úložištěm. Fake se liší od stub právě přítomností spustitelné logiky. Pokud fake-repozitář pouze vrací to, co do něj bylo vloženo, a nezpracovává data — použijte mock nebo stub.
Praktické doporučení — začněte fake pro každý repozitář nebo službu. Pokud je fake složitější než 50 řádků — rozdělte ho do několika tříd. Pokud fake není vůbec potřeba (test kontroluje pouze jeden scénář s pevnými daty) — použijte stub. Pokud test kontroluje, zda byla metoda volána s určitými parametry — použijte mock. Neoptimalizujte výběr předem: napište fake, a pokud se ukáže jako nadbytečný, nahraďte ho stub v konkrétním testu.
Fake-repozitář pro Room — typický příklad fake na Androidu. Produkční implementace UserRepository používá Room DAO s SQLite dotazy. Fake verze ukládá data v MutableList nebo HashMap a implementuje stejné metody: getUser(id), saveUser(user), deleteUser(id). Fake obsahuje logiku vyhledávání, filtrování a řazení — stejnou jako v produkčním repozitáři, ale bez SQL. To umožňuje testovat ViewModel a UseCase bez konfigurace Room databáze.
class FakeUserRepository : UserRepository {
private val users = mutableListOf<User>()
override suspend fun getUser(id: String): User? {
return users.find { it.id == id }
}
override suspend fun saveUser(user: User) {
val index = users.indexOfFirst { it.id == user.id }
if (index >= 0) users[index] = user
else users.add(user)
}
override suspend fun search(query: String): List<User> {
return users.filter {
it.name.contains(query, ignoreCase = true)
}
}
}
Fake pro Retrofit API — místo MockWebServer (který je stub, ne fake) lze vytvořit implementaci ApiService, která vrací data z in-memory kolekce. Rozdíl: MockWebServer zachycuje HTTP a vrací JSON, zatímco fake-ApiService pracuje na úrovni Kotlin rozhraní bez serializace. Fake je rychlejší (žádné parsování JSON) a jednodušší na ladění (pracuje ve stejném procesu, typovaný). Vhodný pro testy, kde HTTP sémantika (stavové kódy, hlavičky) není důležitá.
Fake ve Swift — staví se přes protokoly. Produkční třída implementuje protokol se skutečnou logikou (CoreData, URLSession). Fake struktura implementuje stejný protokol s in-memory úložištěm a zjednodušenou logikou. Swift je jazyk s hodnotovou sémantikou, proto jsou fake struktury neměnné a bezpečné ve vícevláknových testech. To dává výhodu oproti Android analogům: přístup k in-memory datům není třeba synchronizovat.
protocol UserRepositoryProtocol {
func getUser(id: String) async -> User?
func saveUser(user: User) async
}
final class FakeUserRepository: UserRepositoryProtocol {
private var storage: [String: User] = [:]
func getUser(id: String) async -> User? {
return storage[id]
}
func saveUser(user: User) async {
storage[user.id] = user
}
}
final class UserViewModelTests: XCTestCase {
func test_save_and_load() async {
let fake = FakeUserRepository()
let vm = UserViewModel(repository: fake)
let user = User(id: "1", name: "Alice")
await vm.saveUser(user)
let loaded = await vm.getUser(id: "1")
XCTAssertEqual(loaded?.name, "Alice")
}
}
Fake pro CoreData — v iOS projektech lze vytvořit in-memory NSPersistentContainer nastavením description.type = NSInMemoryStoreType. Toto je plnohodnotný CoreData stack pracující v paměti. Takový fake umožňuje testovat NSFetchRequest, predikáty a řazení bez vytváření SQLite souboru. Rychlost: testy na in-memory CoreData se provádějí 5-10 krát rychleji než na diskovém analogu. Nevýhoda: NSManagedObjectModel je třeba konfigurovat pokaždé.
FakeURLProtocol — podtřída URLProtocol pro zachycování síťových požadavků na iOS. Registruje se přes URLProtocol.registerClass(fakeProtocol). Uvnitř obsahuje in-memory slovník URL -> Data a vrací data bez skutečného požadavku. Rozdíl od stub: FakeURLProtocol může kontrolovat tělo požadavku, hlavičky a vracet různé odpovědi v závislosti na vstupních datech. Toto je fake, protože obsahuje logiku směrování požadavků.
Fake jako Test Fixture — přesuňte fake třídy do společného testovacího modulu (androidTest/sharedTest nebo TestSupport). Všechny testy v projektu používají stejný InMemoryUserRepository. To eliminuje duplicitu konfigurace mock objektů v každém testu a zaručuje jednotné chování. Změna logiky fake aktualizuje všechny testy současně. V IT Sectr ukládáme fake třídy v sharedTest/java/com/itSectr/fake/ a připojujeme přes implementation project(:sharedTest).
Fake s předem nastavenými daty — často testy potřebují repozitář, který již obsahuje některé záznamy. Řešení: tovární metoda fakeWithData(vararg items) nebo vestavěná metoda addDefaultData(). Továrna vytvoří fake, naplní ho typickými daty a vrátí objekt připravený k použití. To snižuje boilerplate v testech: místo konfigurace mock volání test jednoduše volá FakeUserRepository.withUsers(alice, bob).
Fake s počítáním volání — někdy je třeba zkontrolovat nejen stav, ale také počet volání. Fake může obsahovat čítače: saveCallCount, getUserCallCount. Test zkontroluje čítač po provedení. Toto je kompromis mezi čistým fake (kontrola stavu) a mock (kontrola interakce). Čítače nekontrolují argumenty a pořadí volání — pouze počet. Pro kontrolu argumentů použijte mock.
Fake s Callback — pro testování asynchronních scénářů může fake přijímat callback při každém volání: beforeGetUser, afterSaveUser. To umožňuje simulovat zpoždění, chyby nebo kontrolovat mezistavy. Tento přístup je užitečný pro testování stavů načítání UI: fake udělá pauzu 100 ms a test zkontroluje, zda obrazovka zobrazuje loader. V produkci callback neexistuje — je to čistě testovací funkcionalita.
Často kladené otázky
Fake obsahuje fungující logiku — filtruje, řadí, počítá. Stub vrací pouze předdefinované odpovědi bez logiky. Pokud má objekt větvení (if/else, when) — je to fake. Pokud obsahuje pouze return values — je to stub. Fake je dražší na údržbu, ale poskytuje realističtější testy.
Když se logika fake neshoduje s produkční logikou. Například FakeUserRepository používá case-sensitive vyhledávání, zatímco produkce case-insensitive. Test projde, ale ve skutečnosti je chyba. Řešení: testujte logiku fake samostatně nebo používejte fakes pouze pro rozhraní s jednoduchou logikou (CRUD operace). Pro složitou logiku pište integrační testy s reálnou databází.
In-memory database — jedna z variant fake. Room.inMemoryDatabaseBuilder() vytváří in-memory SQLite, který se chová jako produkční databáze. Toto je plnohodnotný fake. Ale fake může být také na úrovni repozitáře (bez SQL) a na úrovni sítě (FakeApiService). In-memory databáze je speciální případ fake, kdy je logika maximálně blízká reálné.
Ano, ale opatrně. Fake pro repozitář (data), Mock pro AnalyticsTracker (ověření událostí). Rozdělení podle vrstev: fake pro datovou vrstvu, mock pro vrstvu analytiky/logování. Nedělejte jeden objekt současně fake a mock — to porušuje princip jedné odpovědnosti a mate test.
Testujte fake stejnými testy jako produkční implementaci. Pokud máte UserRepositoryTest, který kontroluje save, get, delete — spusťte ho dvakrát: s FakeUserRepository a s RealUserRepository. To zaručuje, že fake opakuje chování produkční třídy. Pokud se fake začne chovat jinak — test selže na obou implementacích.
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é