Fake — ce este, scopul și cum se utilizează în testare

Autor: IT Sectr Publicat: 2026-04-10 Timp de citire: 9 min

Fake (feik) — o implementare simplificată funcțională a dependenței care se comportă ca un component real, dar folosește stocarea in-memory sau alte mecanisme ușoare în locul infrastructurii de producție. Spre deosebire de stub, fake conține logică de afaceri reală — sortare, filtrare, agregare — doar fără efecte externe. Baza de date in-memory în loc de Room sau HashMap în loc de SharedPreferences — exemple clasice. Mai multe detalii în clasificarea test doubles de Martin Fowler.

Principalele

  • Fake — implementare simplificată funcțională cu logică reală, dar fără dependențe externe
  • Stocare in-memory — depozitul fake stochează date în HashMap, nu în baza de date
  • Diferența de Stub — stub returnează date fixe, fake conține logică executabilă
  • Android — InMemoryUserRepository ca Fake pentru testarea ViewModel și UseCase
  • iOS — FakeNetworkSession cu URLProtocol și date de test în loc de server real

Ce este Fake și de ce este necesar în testare?

Fake — este o implementare completă, dar ușoară a unei interfețe, potrivită pentru testare. Termenul a fost introdus de Gerard Meszaros (2007) în cartea «xUnit Test Patterns». Spre deosebire de stub, care returnează răspunsuri strict prestabilite, fake conține cod executabil: poate sorta o listă, filtra după o condiție, număra înregistrările. Singura diferență față de implementarea de producție — fake lucrează cu date in-memory și nu efectuează operații I/O reale.

Avantajul principal — viteza. Testele cu fake se execută în milisecunde, deoarece nu există acces la disc, rețea sau bază de date. HashMap in-memory funcționează de 100-1000 de ori mai rapid decât Room sau CoreData. În același timp, fake verifică logica reală de afaceri: sortare, filtrare, agregare — tot ceea ce stub nu poate verifica, deoarece stub returnează doar ceea ce i s-a spus. Fake oferă încredere că codul procesează corect datele, nu doar primește un răspuns prestabilit.

Când Fake este preferabil Stub

Fake este preferabil Stub — dacă componentul testat efectuează mai multe operații asupra datelor (a extras, a filtrat, a sortat, a salvat), stub va necesita configurarea fiecărei apelări separat. Fake conține logica în interior — testul doar apelează metodele și verifică rezultatul. În IT Sectr folosim fake pentru toate depozitele în testele unitare: depozitul fake cu HashMap acoperă 90% din scenarii fără configurare Mockito sau MockK.

Fake vs Stub vs Mock: când să alegi ce

Criteriul de alegere — determinați ce verifică testul: starea sau interacțiunea. Dacă testul verifică starea (rezultatul lucrului) și folosește logică — aveți nevoie de fake. Dacă testul are nevoie doar de date de intrare fără logică — este suficient stub. Dacă testul verifică faptul apelării unei metode — aveți nevoie de mock. Amestecarea tipurilor de test doubles într-un singur test complică înțelegerea și crește fragilitatea.

CriteriuFakeStubMock
Prezența logiciiDa (simplificată)NuNu
VitezăRidicatăMaximăRidicată
Verificare comportamentIndirectăNuDa (verify)
ÎntreținereO clasă per interfațăConfigurare pentru fiecare testConfigurare pentru fiecare test
RealismRidicat (codul funcționează)Scăzut (date fixe)Mediu
Risc de alarme falseScăzutMediuRidicat (teste fragile)

Antipattern: Fake care nu este fake — o greșeală frecventă când dezvoltatorul numește fake un obiect care de fapt este stub sau mock. Dacă InMemoryUserRepository nu conține logică (filtrare, sortare) — acesta nu este fake, ci stub cu stocare in-memory. Fake se deosebește de stub tocmai prin prezența logicii executabile. Dacă depozitul fake doar returnează ceea ce i s-a pus și nu procesează date — folosiți mock sau stub.

Regula practică de alegere a test double

Recomandare practică — începeți cu fake pentru fiecare depozit sau serviciu. Dacă fake este mai complex de 50 de linii — împărțiți în mai multe clase. Dacă fake nu este deloc necesar (testul verifică doar un scenariu cu date fixe) — folosiți stub. Dacă testul verifică că o metodă a fost apelată cu anumiți parametri — folosiți mock. Nu optimizați alegerea dinainte: scrieți fake, iar dacă se dovedește redundant, înlocuiți-l cu stub în testul respectiv.

Crearea obiectelor Fake pe Android pentru Room și Retrofit

Depozit Fake pentru Room — exemplu tipic de fake pe Android. Implementarea de producție UserRepository folosește Room DAO cu interogări SQLite. Versiunea fake stochează datele în MutableList sau HashMap și implementează aceleași metode: getUser(id), saveUser(user), deleteUser(id). Fake conține logică de căutare, filtrare și sortare — aceeași ca în depozitul de producție, dar fără SQL. Acest lucru permite testarea ViewModel și UseCase fără configurarea bazei Room.

kotlin
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 pentru Retrofit API — în loc de MockWebServer (care este stub, nu fake) se poate crea o implementare ApiService care returnează date dintr-o colecție in-memory. Diferența: MockWebServer interceptează HTTP și returnează JSON, iar fake-ApiService funcționează la nivelul interfeței Kotlin fără serializare. Fake este mai rapid (fără parsare JSON) și mai simplu de depanat (funcționează în același proces, tipizat). Potrivit pentru teste unde semantica HTTP (coduri de stare, antete) nu este importantă.

FakeSharedPreferences pentru teste rapide

— un alt scenariu frecvent. SharedPreferences de producție scrie pe disc prin commit/apply. Versiunea fake stochează perechi cheie-valoare în HashMap și returnează instantaneu datele. Suportă aceleași metode: getString, putString, getInt, putInt, clear. Pentru Jetpack DataStore analogul — FakeDataStore cu stocare in-memory. Astfel de fake accelerează testele de zeci de ori, deoarece nu există operații de scriere pe disc.

Implementări Fake pe iOS cu stocări in-memory

Fake în Swift — se construiește prin protocoale. Clasa de producție implementează protocolul cu logică reală (CoreData, URLSession). Structura fake implementează același protocol cu stocare in-memory și logică simplificată. Swift este un limbaj cu semantică de valoare, deci structurile fake sunt imutabile și sigure în teste multi-thread. Acest lucru oferă un avantaj față de analogii pe Android: nu este nevoie să sincronizați accesul la datele in-memory.

swift
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 pentru CoreData — în proiectele iOS se poate crea un NSPersistentContainer in-memory, setând description.type = NSInMemoryStoreType. Acesta este un stack CoreData complet care funcționează în memorie. Un astfel de fake permite testarea NSFetchRequest, predicatelor și sortărilor fără a crea un fișier SQLite. Viteză: testele pe CoreData in-memory se execută de 5-10 ori mai rapid decât pe analogul pe disc. Dezavantaj: trebuie configurat NSManagedObjectModel de fiecare dată.

FakeURLProtocol — o subclasă a URLProtocol pentru interceptarea cererilor de rețea pe iOS. Se înregistrează prin URLProtocol.registerClass(fakeProtocol). În interior conține un dicționar in-memory URL -> Data și returnează date fără cerere reală. Diferența de stub: FakeURLProtocol poate verifica corpul cererii, antetele și poate returna răspunsuri diferite în funcție de datele de intrare. Acesta este fake deoarece conține logică de rutare a cererilor.

Pattern-uri de utilizare Fake în proiecte mobile

Fake ca Test Fixture — mutați clasele fake într-un modul de test comun (androidTest/sharedTest sau TestSupport). Toate testele din proiect folosesc același InMemoryUserRepository. Acest lucru elimină duplicarea configurării obiectelor mock în fiecare test și garantează un comportament uniform. Modificarea logicii fake actualizează toate testele simultan. În IT Sectr păstrăm clasele fake în sharedTest/java/com/itSectr/fake/ și le conectăm prin implementation project(:sharedTest).

Fake cu date prestabilite — adesea testele au nevoie de un depozit care conține deja unele înregistrări. Soluție: metoda fabrică fakeWithData(vararg items) sau metoda incorporată addDefaultData(). Fabrica creează fake, îl umple cu date tipice și returnează obiectul gata de utilizare. Acest lucru reduce boilerplate-ul în teste: în loc să configurați apelări mock, testul doar apelează FakeUserRepository.withUsers(alice, bob).

Fake cu numărare de apeluri — uneori trebuie verificat nu doar starea, ci și numărul de accesări. Fake poate conține contoare: saveCallCount, getUserCallCount. Testul verifică contorul după execuție. Acesta este un compromis între fake pur (verificarea stării) și mock (verificarea interacțiunii). Contoarele nu verifică argumentele și ordinea apelurilor — doar numărul. Pentru verificarea argumentelor folosiți mock.

Fake cu Callback — pentru testarea scenariilor asincrone, fake poate accepta un callback la fiecare apel: beforeGetUser, afterSaveUser. Acest lucru permite simularea întârzierilor, erorilor sau verificarea stărilor intermediare. O astfel de abordare este utilă pentru testarea stărilor de încărcare UI: fake face o pauză de 100 ms, iar testul verifică dacă ecranul arată un loader. În producție callback-ul lipsește — este o funcționalitate pur de test.

Întrebări frecvente

Cu ce se deosebește Fake de Stub?

Fake conține logică funcțională — filtrează, sortează, numără. Stub returnează doar răspunsuri prestabilite fără logică. Dacă obiectul are ramificări (if/else, when) — acesta este fake. Dacă conține doar return values — este stub. Fake este mai scump de întreținut, dar oferă teste mai realiste.

Când poate fi dăunător fake?

Când logica fake nu coincide cu logica de producție. De exemplu, FakeUserRepository folosește căutare case-sensitive, iar producția — case-insensitive. Testul trece, dar în realitate există o eroare. Soluție: testați logica fake separat sau folosiți fakes doar pentru interfețe cu logică simplă (operații CRUD). Pentru logică complexă scrieți teste de integrare cu baza de date reală.

Fake este același lucru cu in-memory database?

In-memory database — una dintre variantele de fake. Room.inMemoryDatabaseBuilder() creează un SQLite in-memory care se comportă ca o bază de date de producție. Acesta este un fake complet. Dar fake poate fi și la nivel de depozit (fără SQL) și la nivel de rețea (FakeApiService). Baza de date in-memory este un caz particular de fake, când logica este cât mai aproape de cea reală.

Se pot combina Fake și Mock într-un singur test?

Da, dar cu prudență. Fake pentru depozit (date), Mock pentru AnalyticsTracker (verificare evenimente). Separare pe straturi: fake pentru stratul de date, mock pentru stratul de analitică/logare. Nu faceți un obiect simultan fake și mock — aceasta încalcă principiul responsabilității unice și complică înțelegerea testului.

Cum se testează Fake însuși?

Testați fake cu aceleași teste ca și implementarea de producție. Dacă aveți UserRepositoryTest care verifică save, get, delete — rulați-l de două ori: cu FakeUserRepository și cu RealUserRepository. Aceasta garantează că fake reproduce comportamentul clasei de producție. Dacă fake începe să se comporte diferit — testul va eșua pe ambele implementări.

Concluzii

  • Fake — implementare simplificată funcțională a dependenței cu logică de afaceri reală și stocare in-memory
  • Diferența de Stub — fake conține logică (filtrare, sortare), stub doar returnează date
  • Viteză — fake funcționează de 100-1000 de ori mai rapid decât implementarea de producție fără operații I/O
  • Android — InMemoryUserRepository, FakeDataStore, in-memory Room prin Room.inMemoryDatabaseBuilder
  • iOS — fake bazat pe protocol, CoreData in-memory, FakeURLProtocol pentru interceptare HTTP
  • Cea mai bună practică — mutați fake în modulul de test comun și folosiți-l în toate testele proiectului
  • Testați fake — rulați aceleași teste pe fake și implementarea de producție pentru verificarea consistenței

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și