Test Doubles — tipi di sostituti e applicazione

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

I Test Doubles sono oggetti sostituti utilizzati nei test unitari al posto delle dipendenze reali. Il termine è stato introdotto da Gerard Meszaros nel libro “xUnit Test Patterns” (2007) come concetto generale per Mock, Stub, Fake, Spy e Dummy. Secondo Martin Fowler (2024), i Test Doubles consentono di isolare il componente sotto test dal suo ambiente, rendendo i test deterministici, veloci e indipendenti dai servizi esterni.

Punti chiave

  • Test Doubles — termine generale per tutti i tipi di oggetti sostituti nei test
  • Mock verifica l'interazione: quali metodi sono stati chiamati e con quali argomenti
  • Stub restituisce valori predefiniti senza verificare le chiamate
  • Fake — implementazione funzionale semplificata (es., database in memoria)
  • Spy registra le chiamate per una verifica successiva, Dummy riempie i parametri

Cosa sono i Test Doubles?

Test Doubles è un termine dell'industria automobilistica (controfigura), trasferito nello sviluppo software. Come una controfigura sostituisce un attore in una scena pericolosa, un Test Double sostituisce un componente reale in uno scenario di test. Ciò è necessario quando la dipendenza reale non è disponibile, è lenta, non deterministica o ha effetti collaterali.

Il concetto di Test Double comprende cinque tipi specifici, ognuno dei quali risolve il proprio compito. La tipologia di Meszaros è canonica e utilizzata in tutte le guide moderne sui test. La differenza tra i tipi risiede nel grado di controllo e verifica: dal semplice riempimento di parametri (Dummy) alla verifica completa della sequenza di chiamate (Mock).

Perché sono necessari i Test Doubles

Lo scopo principale dei Test Doubles è isolare il modulo sotto test. Nello sviluppo mobile, le dipendenze reali includono server API, database, file system, sensori del dispositivo e servizi di sistema (LocationManager, Camera, Bluetooth). L'uso diretto di questi componenti rende i test lenti, fragili e dipendenti dall'ambiente. Secondo Google Testing Blog (2023), i test unitari ben isolati vengono eseguiti in millisecondi, mentre i test di integrazione vengono eseguiti in secondi e minuti.

Cinque tipi di Test Doubles

La classificazione di Gerard Meszaros include cinque tipi di Test Doubles, che differiscono per comportamento e scopo. Comprendere la differenza tra loro è la base per test unitari competenti.

Dummy

Dummy è un oggetto che viene passato al metodo sotto test ma non viene mai utilizzato. Dummy è necessario solo per soddisfare la firma del metodo. In Kotlin, questo è spesso null, emptyList() o un oggetto con stub. Dummy non deve contenere alcuna logica — se viene chiamato, il test deve fallire.

Fake

Fake è un'implementazione semplificata ma funzionante di un'interfaccia. A differenza di Mock e Stub, Fake contiene logica di business reale, ma in forma semplificata. Un esempio classico è InMemoryUserRepository, che memorizza i dati in una HashMap invece di un database. Fake viene utilizzato quando è necessario testare logica che dipende dallo stato, ma senza il sovraccarico dell'infrastruttura reale.

TipoScopoEsempio
DummyRiempire un parametronull, oggetto vuoto
FakeImplementazione semplificata funzionanteInMemoryRepository
StubRestituire un valore fissowhen(api.getUser()).thenReturn(user)
SpyRegistrare chiamate per verificaverify(spy).save(user)
MockVerificare l'interazioneverify(mock).sendEmail(email)

Stub

Stub restituisce valori predefiniti per chiamate specifiche. Stub non verifica se è stato chiamato — fornisce semplicemente dati. In Mockito, Stub viene creato tramite when(method).thenReturn(value). Stub è ideale per test quando è necessario che una dipendenza restituisca un valore specifico, ma il fatto stesso della chiamata non è importante.

Spy

Spy è un involucro attorno a un oggetto reale che registra tutte le chiamate per la verifica successiva. A differenza di Mock, Spy delega le chiamate all'oggetto reale ma consente di verificare che siano avvenute. In Mockito, Spy viene creato tramite spy(realObject). Spy è utile per il mocking parziale, quando si desidera utilizzare un oggetto reale ma verificare alcune chiamate.

Mock

Mock è un oggetto con aspettative di chiamata predefinite. Mock verifica che metodi specifici siano stati chiamati con argomenti specifici e in un ordine specifico. A differenza di Stub, Mock si concentra sulla verifica del comportamento piuttosto che sulla restituzione dei dati. Mock è il tipo di Test Double più potente e più utilizzato nello sviluppo mobile.

Mock vs Stub: differenze chiave

La differenza tra Mock e Stub causa spesso confusione anche tra sviluppatori esperti. La differenza principale risiede nello scopo: Stub verifica lo stato (state verification), Mock verifica il comportamento (behavior verification).

Stub risponde alla domanda: “il codice ha restituito il risultato corretto?”. Mock risponde alla domanda: “il codice ha chiamato i metodi corretti con gli argomenti corretti?”. Nello sviluppo mobile, Stub viene utilizzato quando il risultato è importante (es., dati da un repository), mentre Mock viene utilizzato quando gli effetti collaterali sono importanti (es., inviare email, scrivere su un database).

kotlin
// Stub: verifica dello stato
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)

// Mock: verifica del comportamento
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }

Esempi di Test Doubles in Kotlin

Esempi pratici di tutti e cinque i tipi di Test Doubles in Kotlin utilizzando MockK — la libreria di mocking più popolare per progetti Android.

Fake: InMemoryUserRepository

kotlin
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]
    }
}

Stub + Mock: test di UseCase

kotlin
class RegisterUseCaseTest {
    private val api = mockk<AuthApi>()
    private val repo = spyk(InMemoryUserRepository())
    private val useCase = RegisterUseCase(api, repo)

    fun `register user successfully`() = runTest {
        // Stub: restituire risposta API fissa
        coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")

        val result = useCase.execute("test@test.com")

        // Verify: verificare che l'utente sia stato salvato
        verify { repo.save(any()) }
        assertTrue(result.isSuccess())
    }
}

Dummy: test con parametro non utilizzato

kotlin
data class Logger(val appContext: Context, val format: FormatType)

fun `test logger with dummy context`() {
    // Dummy: Context non viene utilizzato all'interno di Logger
    val dummyContext = mockk<Context>()
    val logger = Logger(dummyContext, FormatType.JSON)
    assertEquals(FormatType.JSON, logger.format)
}

Quando usare ogni tipo nello sviluppo mobile

La scelta del tipo di Test Double dipende da cosa viene esattamente testato: stato, comportamento o integrazione. Nello sviluppo mobile Android e iOS, sono state stabilite le seguenti raccomandazioni.

Per ViewModel e UseCase

Quando si testa ViewModel, utilizzare Mock per le dipendenze che producono effetti collaterali (repository, analytics, navigazione) e Stub per le dipendenze che restituiscono dati (client API, ContentProvider). Ciò consente di verificare che il ViewModel gestisca correttamente sia gli scenari di successo che quelli di errore.

Per Repository e livello dati

A livello di Repository, preferire Fake (implementazioni di database in memoria) e Stub (risposte API fisse). Fake consente di testare la logica di caching e la modalità offline senza configurare SQLite. Stub simula vari stati HTTP: 200, 404, 500, timeout.

  • Test unitari di logica di business — Mock per tutte le dipendenze esterne, Dummy per parametri non utilizzati
  • Test di integrazione — Fake invece di Mock (verificare che i componenti funzionino insieme)
  • Test UI — Stub per le risposte API (tramite MockWebServer o WireMock)
  • Test di caching — Fake per il database (in memoria invece di Room/SQLite)
  • Test di asincronia — Mock con supporto per coroutine (MockK + Turbine per Flow)

Errori comuni nell'uso di sostituti

L'uso scorretto dei Test Doubles è una delle cause più comuni di test fragili che si rompono a ogni refactoring.

Over-mocking: uso eccessivo di Mock

L'errore più comune è mockare tutto. Se ogni dipendenza in un test viene sostituita con un Mock, il test smette di verificare il comportamento reale. Mock dovrebbe essere usato solo per dipendenze esterne (rete, database, file system, servizi di sistema). I componenti interni dell'applicazione (Value Object, data class, semplici utility) non dovrebbero essere sostituiti.

Under-specification: specifica insufficiente

Il secondo errore è creare un Mock senza definire le aspettative. Se un metodo viene chiamato senza every / when, Mock restituisce un valore predefinito (null, 0, false). Ciò può portare a test falso-positivi, in cui Mock restituisce silenziosamente null e il test interpreta questo come comportamento corretto.

Over-verification: verifica eccessiva

Il terzo errore è verificare ogni chiamata di ogni Mock. Verify dovrebbe essere usato solo per chiamate che sono criticamente importanti dal punto di vista della logica di business. La verifica eccessiva rende i test fragili: cambiare l'ordine delle chiamate nel codice di produzione rompe i test senza modificare il comportamento.

Domande frequenti

Qual è la differenza tra Mock e Stub?

Stub restituisce dati e verifica lo stato (cosa è stato restituito), mentre Mock verifica il comportamento (quali metodi sono stati chiamati). Stub = “restituisci X”, Mock = “verifica che Y sia stato chiamato con argomento Z”. Nei test reali, un oggetto spesso funge contemporaneamente da Stub e Mock.

Quando usare Fake invece di Mock?

Fake è preferibile a Mock quando si testa logica che dipende dallo stato: caching, modalità offline, transazioni. Fake (implementazione in memoria) consente di testare questi scenari senza fragili chiamate verify. Mock è più adatto per verificare l'invio di dati: analytics, push, email.

Quale libreria di Test Doubles è la migliore per Android?

Per progetti Android in Kotlin, si consiglia MockK. Supporta coroutine, funzioni suspend, classi sealed e funzioni di estensione senza configurazione aggiuntiva. Per progetti Java, Mockito rimane lo standard — la libreria più popolare con documentazione estesa.

Come testare Kotlin Flow con Test Doubles?

Per testare Kotlin Flow, utilizzare la libreria Turbine insieme a MockK. Turbine semplifica la verifica delle emissioni di Flow: è possibile verificare l'ordine dei valori, il completamento del flusso e le eccezioni. Stub per Flow restituisce flowOf(value), Mock verifica che il Flow sia stato raccolto.

È accettabile usare Test Doubles nei test UI?

Sì, ma a livello di risposte API, non di componenti UI. Le librerie MockWebServer (OkHttp) e WireMock consentono di simulare risposte HTTP nei test UI. I componenti UI stessi (Compose, SwiftUI Views) non dovrebbero essere sostituiti — il loro comportamento viene testato tramite screenshot test ed Espresso.

Riepilogo

  • Test Doubles — termine generale per cinque tipi di oggetti sostituti: Mock, Stub, Fake, Spy, Dummy
  • Mock verifica il comportamento (verify), Stub restituisce dati (thenReturn), Fake funge da implementazione reale semplificata
  • Spy avvolge un oggetto reale e registra le chiamate, Dummy riempie parametri non utilizzati
  • La tipologia di Gerard Meszaros è la classificazione canonica utilizzata in tutti i framework di mocking moderni
  • Per progetti Kotlin si consiglia MockK, per Java — Mockito, per iOS — Cuckoo o OHHTTPStubs
  • Errori comuni: over-mocking (sostituire tutto), under-specification (aspettative indefinite), over-verification (verify eccessivo)
  • Fake è preferibile a Mock quando si testa logica con stato — caching, modalità offline e transazioni

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