Perché è difficile testare un sistema che dipende dai singleton?

Ho letto casi a favore e contro l’utilizzo del modello singleton. Un caso comune contro descrive le difficoltà nei test unitari con i singleton, ma non sono chiaro perché sia ​​così? Se il test unitario fa parte della build non potresti semplicemente fare riferimento al singleton e usarlo quando è necessario? (Sto pensando da una prospettiva java ma immagino che non dovrebbe importare)

Un grande articolo su questo è Singletons is Pathological Liars . Questo descrive, usando un semplice esempio, perché testare usando i singletons è inaspettatamente difficile.

Se si fa riferimento a una class singleton che esiste al di fuori della class sotto test, allora non si ha più un vero test unitario. Invece di testare una singola unità – la class target – ora stai testando due unità: la class target e il singleton.

C’è anche il fatto che gli oggetti singleton tendono ad avere uno stato. Affinché i test unitari siano ripetibili, è necessario eseguire il rollback di tali cambiamenti di stato al termine del test dell’unità. In alternativa, devi creare una versione fittizia del singleton che viene distrutta dopo l’esecuzione di ogni test. O aggiunge una notevole quantità di overhead, sia nel codice sorgente che nel tempo di esecuzione.

Perché il singleton è una variabile globale OOPish. Fondamentalmente, tutte le funzioni che si basano sull’uso del singleton (direttamente o indirettamente) non sono garantite come deterministiche (ovvero non ci si può aspettare che la funzione restituisca le stesse uscite per gli stessi ingressi T ogni e ogni esecuzione).

TL; DR Lo stesso object è condiviso tra tutti i test, che può diventare un dolore.

Se il singleton contiene uno stato e si eseguono più test su di esso, l’ordinamento dei test potrebbe diventare un problema. Immagina un singleton “MailStore”, che contiene un elenco di messaggi. Voglio scrivere un test unitario per elencare i messaggi e un altro per eliminarli.

Ovviamente se la “lista” viene eseguita prima dell’eliminazione, forse è ok. Se “cancella” viene eseguito prima della “lista”, allora abbiamo difficoltà perché non c’è nulla da eliminare. (I risultati cambiano in base all’ordine in cui vengono eseguiti i test).

Questo Google Techtalk fa un ottimo lavoro nel descrivere i problemi con i singleton e lo stato globale quando si tratta di test delle unità.

I singleton sono un problema per diversi motivi:

  • Sono un caso speciale di Service Locator, che fornisce un meccanismo per “prendermi uno di quelli” che non è necessariamente facile da ignorare quando necessario.
  • Forniscono un punto di ingresso per leggere o scrivere variabili globali. Il raggruppamento di tali variabili globali in un object con una sola istanza accessibile a livello globale tramite il modello Singleton non comporta magicamente il blocco delle variabili globali.
  • Sono problemi anche per la manutenzione. Ad esempio, cosa succede quando smettono di essere un vero Singleton? Forse devi accedere a due database invece che al “database”?

Non ricordo di averlo mai letto, ma sospetto che il problema sia il fatto che puoi crearne uno solo. In alcuni casi, questo potrebbe non essere un problema, basta testarlo normalmente.

Ma cosa succede se si desidera creare e testarne uno diverso, magari con diversi parametri del metodo costruttore / fabbrica? Riavvia la JVM? O crea il tuo singleton in modo che non sia realmente un singleton e possa essere resettato? Non bene.