Fake — cos’è, scopo e come usarlo nei test

Autore: IT Sectr Pubblicato: 2026-04-10 Tempo di lettura: 9 min

Fake è un’implementazione semplificata e funzionante di una dipendenza che si comporta come un componente reale, ma utilizza memoria interna o altri meccanismi leggeri invece dell’infrastruttura di produzione. A differenza di uno stub, un fake contiene logica di business reale — ordinamento, filtraggio, aggregazione — ma senza effetti esterni. Un database in memoria invece di Room o una HashMap invece di SharedPreferences sono esempi classici. Maggiori dettagli in classificazione dei test doubles di Martin Fowler.

Punti chiave

  • Fake — un’implementazione semplificata e funzionante con logica reale ma senza dipendenze esterne
  • Memoria interna — un repository fake memorizza i dati in una HashMap invece di un DB
  • Differenza da Stub — stub restituisce dati fissi, fake contiene logica eseguibile
  • Android — InMemoryUserRepository come Fake per testare ViewModel e UseCase
  • iOS — FakeNetworkSession con URLProtocol e dati di test invece di un server reale

Cos’è un Fake e perché serve nei test?

Fake è un’implementazione completa ma leggera di un’interfaccia, adatta ai test. Il termine è stato introdotto da Gerard Meszaros (2007) nel libro “xUnit Test Patterns.” A differenza di uno stub, che restituisce risposte predefinite, un fake contiene codice eseguibile: può ordinare una lista, filtrare per condizione, contare record. L’unica differenza dall’implementazione di produzione è che un fake lavora con dati in memoria e non esegue operazioni di I/O reali.

Il vantaggio principale è la velocità. I test con un fake vengono eseguiti in millisecondi perché non c’è accesso a disco, rete o database. Una HashMap in memoria funziona 100–1000 volte più veloce di Room o CoreData. Allo stesso tempo, un fake testa la logica di business reale: ordinamento, filtraggio, aggregazione — tutto ciò che uno stub non può testare perché restituisce solo ciò che gli è stato detto. Un fake dà la certezza che il codice elabora correttamente i dati, invece di ricevere semplicemente una risposta predeterminata.

Quando Fake è preferibile a Stub

Fake è preferibile a Stub — se il componente sotto test esegue più operazioni sui dati (recupero, filtraggio, ordinamento, salvataggio), uno stub richiederebbe di configurare ogni chiamata individualmente. Un fake contiene la logica al suo interno — il test chiama semplicemente i metodi e verifica il risultato. In IT Sectr, usiamo fakes per tutti i repository nei test unitari: un repository fake con una HashMap copre il 90% degli scenari senza configurare Mockito o MockK.

Fake vs Stub vs Mock: quando scegliere cosa

Criterio di selezione — determina cosa verifica il test: stato o interazione. Se il test verifica lo stato (il risultato del lavoro) e usa logica — usa un fake. Se il test ha bisogno solo di dati di input senza logica — uno stub è sufficiente. Se il test verifica che un metodo è stato chiamato — usa un mock. Mescolare tipi di test doubles in un test complica la comprensione e aumenta la fragilità.

CriterioFakeStubMock
Ha logicaSì (semplificata)NoNo
VelocitàAltaMassimaAlta
Verifica del comportamentoIndirettaNoSì (verify)
ManutenzioneUna classe per interfacciaConfigurare per testConfigurare per test
RealismoAlto (il codice funziona)Basso (dati fissi)Medio
Rischio falsi positiviBassoMedioAlto (test fragili)

Anti-pattern: Fake che non è un fake — un errore comune quando uno sviluppatore chiama fake un oggetto che in realtà è uno stub o un mock. Se il tuo InMemoryUserRepository non contiene logica (filtraggio, ordinamento) — non è un fake, ma uno stub con memoria interna. Un fake si differenzia da uno stub proprio per la presenza di logica eseguibile. Se un repository fake restituisce semplicemente ciò che vi è stato inserito e non elabora i dati — usa un mock o uno stub.

Regola pratica per scegliere un Test Double

Raccomandazione pratica — inizia con un fake per ogni repository o servizio. Se un fake supera 50 righe — dividilo in più classi. Se un fake non è affatto necessario (il test verifica un solo scenario con dati fissi) — usa uno stub. Se il test verifica che un metodo è stato chiamato con parametri specifici — usa un mock. Non ottimizzare la scelta in anticipo: scrivi un fake e, se risulta eccessivo, sostituiscilo con uno stub in un test specifico.

Creazione di oggetti Fake su Android per Room e Retrofit

Repository fake per Room — un tipico esempio di fake su Android. L’implementazione di produzione di UserRepository usa Room DAO con query SQLite. La versione fake memorizza i dati in una MutableList o HashMap e implementa gli stessi metodi: getUser(id), saveUser(user), deleteUser(id). Il fake contiene logica di ricerca, filtraggio e ordinamento — identica al repository di produzione, ma senza SQL. Ciò consente di testare ViewModel e UseCase senza configurare un database 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 per API Retrofit — invece di MockWebServer (che è uno stub, non un fake), puoi creare un’implementazione di ApiService che restituisce dati da una collezione in memoria. La differenza: MockWebServer intercetta HTTP e restituisce JSON, mentre un fake ApiService funziona a livello di interfaccia Kotlin senza serializzazione. Un fake è più veloce (nessun parsing JSON) e più facile da debuggare (gira nello stesso processo, tipizzato). Adatto per test dove la semantica HTTP (codici di stato, header) non è importante.

FakeSharedPreferences per test veloci

— un altro scenario comune. Le SharedPreferences di produzione scrivono su disco tramite commit/apply. La versione fake memorizza coppie chiave-valore in una HashMap e restituisce i dati istantaneamente. Supporta gli stessi metodi: getString, putString, getInt, putInt, clear. Per Jetpack DataStore, l’analogo è FakeDataStore con memoria interna. Questi fakes accelerano i test decine di volte perché non ci sono operazioni di scrittura su disco.

Implementazioni Fake su iOS con memoria interna

Fake in Swift — si costruisce tramite protocolli. La classe di produzione implementa il protocollo con logica reale (CoreData, URLSession). La struttura fake implementa lo stesso protocollo con memoria interna e logica semplificata. Swift è un linguaggio con semantica di valore, quindi le strutture fake sono immutabili e sicure nei test multithread. Questo dà un vantaggio rispetto agli analoghi Android: non è necessario sincronizzare l’accesso ai dati in memoria.

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 per CoreData — nei progetti iOS, puoi creare un NSPersistentContainer in memoria impostando description.type = NSInMemoryStoreType. Questo è uno stack CoreData completo, ma funzionante in memoria. Tale fake consente di testare NSFetchRequest, predicati e ordinamenti senza creare un file SQLite. Velocità: i test su CoreData in memoria vengono eseguiti 5–10 volte più velocemente rispetto all’analogo su disco. Svantaggio: è necessario configurare NSManagedObjectModel ogni volta.

FakeURLProtocol — una sottoclasse di URLProtocol per intercettare richieste di rete su iOS. Viene registrata tramite URLProtocol.registerClass(fakeProtocol). Internamente contiene un dizionario URL -> Data in memoria e restituisce dati senza una richiesta reale. La differenza da uno stub: FakeURLProtocol può verificare il corpo della richiesta, gli header e restituire risposte diverse in base ai dati di input. Questo è un fake perché contiene logica di routing delle richieste.

Pattern di utilizzo di Fake in progetti mobili

Fake come Test Fixture — metti le classi fake in un modulo di test condiviso (androidTest/sharedTest o TestSupport). Tutti i test del progetto usano lo stesso InMemoryUserRepository. Ciò elimina la duplicazione della configurazione di oggetti mock in ogni test e garantisce un comportamento uniforme. Modificare la logica del fake aggiorna tutti i test simultaneamente. In IT Sectr, memorizziamo le classi fake in sharedTest/java/com/itSectr/fake/ e le includiamo tramite implementation project(:sharedTest).

Fake con dati preimpostati — spesso i test necessitano di un repository che già contenga alcuni record. Soluzione: un metodo factory fakeWithData(vararg items) o un metodo integrato addDefaultData(). La factory crea un fake, lo riempie con dati tipici e restituisce un oggetto pronto all’uso. Ciò riduce il boilerplate nei test: invece di configurare chiamate mock, il test chiama semplicemente FakeUserRepository.withUsers(alice, bob).

Fake con conteggio delle chiamate — a volte è necessario verificare non solo lo stato ma anche il numero di invocazioni. Un fake può contenere contatori: saveCallCount, getUserCallCount. Il test verifica il contatore dopo l’esecuzione. Questo è un compromesso tra un fake puro (verifica dello stato) e un mock (verifica dell’interazione). I contatori non verificano argomenti o ordine delle chiamate — solo il numero. Per la verifica degli argomenti, usa un mock.

Fake con Callback — per testare scenari asincroni, un fake può accettare un callback a ogni chiamata: beforeGetUser, afterSaveUser. Ciò consente di simulare ritardi, errori o verificare stati intermedi. Questo approccio è utile per testare gli stati di caricamento dell’interfaccia: il fake si mette in pausa per 100 ms e il test verifica che lo schermo mostri un indicatore di caricamento. Il callback è assente in produzione — è funzionalità puramente di test.

Domande frequenti

Qual è la differenza tra Fake e Stub?

Fake contiene logica funzionale — filtra, ordina, conta. Stub restituisce solo risposte predeterminate senza logica. Se un oggetto ha ramificazioni (if/else, when) — è un fake. Se contiene solo valori di ritorno — è uno stub. Un fake è più costoso da mantenere ma fornisce test più realistici.

Quando un fake può essere dannoso?

Quando la logica del fake non corrisponde alla logica di produzione. Ad esempio, FakeUserRepository usa una ricerca case-sensitive, mentre la versione di produzione è case-insensitive. Il test passa, ma in realtà c’è un bug. Soluzione: testa la logica del fake separatamente o usa fakes solo per interfacce con logica semplice (operazioni CRUD). Per logica complessa, scrivi test di integrazione con un database reale.

Fake è la stessa cosa di un database in memoria?

Database in memoria è un tipo di fake. Room.inMemoryDatabaseBuilder() crea SQLite in memoria che si comporta come un database di produzione. Questo è un fake a tutti gli effetti. Ma un fake può esistere anche a livello di repository (senza SQL) e a livello di rete (FakeApiService). Un database in memoria è un caso speciale di fake in cui la logica è il più vicino possibile a quella reale.

Posso combinare Fake e Mock in un test?

Sì, ma con cautela. Fake per il repository (dati), Mock per AnalyticsTracker (verifica eventi). Separazione per livelli: fake per il livello dati, mock per il livello di analisi/log. Non fare di un oggetto contemporaneamente un fake e un mock — viola il Principio di Responsabilità Unica e confonde il test.

Come testare il Fake stesso?

Testa il fake con gli stessi test dell’implementazione di produzione. Se hai un UserRepositoryTest che verifica save, get, delete — eseguilo due volte: con FakeUserRepository e con RealUserRepository. Ciò garantisce che il fake replichi il comportamento della classe di produzione. Se il fake inizia a comportarsi diversamente — il test fallirà su entrambe le implementazioni.

Riepilogo

  • Fake — un’implementazione semplificata e funzionante di una dipendenza con logica di business reale e memoria interna
  • Differenza da Stub — fake contiene logica (filtraggio, ordinamento), stub restituisce solo dati
  • Velocità — fake funziona 100–1000 volte più veloce dell’implementazione di produzione senza operazioni di I/O
  • Android — InMemoryUserRepository, FakeDataStore, Room in memoria tramite Room.inMemoryDatabaseBuilder
  • iOS — fake basato su protocolli, CoreData in memoria, FakeURLProtocol per intercettazione HTTP
  • Migliore pratica — metti i fakes in un modulo di test condiviso e usali in tutti i test del progetto
  • Testa il fake — esegui gli stessi test sul fake e sull’implementazione di produzione per verificare la coerenza

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche