Come posso evitare che i miei test unitari richiedano conoscenze sugli interni dell'implementazione quando utilizzo oggetti fittizi?

StackOverflow https://stackoverflow.com/questions/3448121

Domanda

Sono ancora in fase di apprendimento per quanto riguarda i test unitari e in particolare per quanto riguarda il mocking (sto usando il file PascalMock E Unità D quadri).Una cosa su cui ora mi sono imbattuto è che non sono riuscito a trovare un modo per codificare i dettagli di implementazione della classe/interfaccia testata nel mio test unitario e questo sembra semplicemente sbagliato...

Per esempio:Voglio testare una classe che implementa un'interfaccia molto semplice per leggere e scrivere le impostazioni dell'applicazione (sostanzialmente coppie nome/valore).L'interfaccia presentata al consumatore è completamente indipendente da dove e come i valori vengono effettivamente archiviati (ad es.registro, file INI, XML, database, ecc.).Naturalmente, il livello di accesso è implementato da una classe ancora diversa che viene inserita nella classe testata durante la costruzione.Ho creato un oggetto fittizio per questo livello di accesso e ora sono in grado di testare completamente la classe di implementazione dell'interfaccia senza leggere o scrivere nulla su alcun registro/file INI/qualsiasi cosa.

Tuttavia, per garantire che il mock si comporti esattamente come quello reale quando vi accede la classe testata, i miei test unitari devono impostare l'oggetto mock definendo in modo molto esplicito le chiamate al metodo previste e i valori restituiti attesi dalla classe testata.Ciò significa che se mai dovessi apportare modifiche all'interfaccia del livello di accesso o al modo in cui la classe testata utilizza quel livello dovrò modificare anche gli unit test per la classe che utilizza internamente quell'interfaccia anche se il interfaccia della classe che sto attualmente testando non è cambiata affatto.È qualcosa con cui dovrò convivere quando utilizzo i mock o esiste un modo migliore per progettare le dipendenze delle classi che lo eviterebbero?

È stato utile?

Soluzione

È qualcosa con cui dovrò solo convivere quando usi le beffe o c'è un modo migliore per progettare le dipendenze della classe che lo eviterebbero?

Molte volte i mock (framework particolarmente sensibili come JMock) ti costringono a tenere conto dei dettagli che non si riferiscono direttamente al comportamento che stai cercando di testare, e talvolta questo può anche essere utile esponendo codice sospetto che sta facendo troppo e ha troppe chiamate/dipendenze.

Tuttavia nel tuo caso, se ho letto bene la tua descrizione, sembra che tu non abbia davvero problemi.Se progetti il ​​livello di lettura/scrittura correttamente e con un livello di astrazione appropriato, non dovresti doverlo modificare.

Ciò significa che se dovessi mai apportare modifiche all'interfaccia del livello di accesso o al modo in cui la classe testata utilizza quel livello I dovrò anche modificare i test unitari per la classe che utilizza internamente quell'interfaccia anche se l'interfaccia Della classe che sto effettivamente testando non è affatto cambiato.

Non è il punto di scrivere il livello di accesso astratto per evitarlo?In generale, seguendo il Principio aperto/chiuso, un'interfaccia di questo tipo non dovrebbe change e non dovrebbe interrompere il contratto con la classe che lo consuma e, per estensione, non interromperà nemmeno i test unitari.Ora, se modifichi l'ordine delle chiamate al metodo o devi effettuare nuove chiamate al livello astratto, allora sì, in particolare con alcuni framework, le tue finte aspettative verranno infrante.Questa è solo una parte del costo dell'utilizzo dei mock ed è perfettamente accettabile.Ma l'interfaccia stessa dovrebbe, in generale, rimanere stabile.

Altri suggerimenti

per garantire che il mock si comporti esattamente come quello reale quando vi accede la classe testata, i miei test unitari devono impostare l'oggetto mock definendo in modo molto esplicito le chiamate al metodo previste e i valori restituiti attesi dalla classe testata.

Corretto.

modifiche all'interfaccia del livello di accesso o al modo in cui la classe testata utilizza quel livello dovrò modificare anche gli unit test

Corretto.

anche se l'interfaccia della classe che sto attualmente testando non è cambiata affatto.

"Veramente testando"?Intendi la classe dell'interfaccia esposta?Va bene.

Il modo in cui la classe "testata" (interfaccia) utilizza il livello di accesso significa che hai modificato l'interfaccia interna nel livello di accesso.Le modifiche all'interfaccia (anche quelle interne) richiedono modifiche di prova e possono portare a rotture se hai fatto qualcosa di sbagliato.

Non c'è niente di sbagliato in questo.In effetti, il punto è che qualsiasi modifica al livello di accesso dovere richiedere modifiche ai mock per garantire che la modifica "funzioni".

I test non dovrebbero essere "robusti".Dovrebbe essere fragile.Se apporti un cambiamento che altera il comportamento interno, allora le cose Potere rottura.Se i tuoi test fossero troppo robusti non testerebbero nulla: funzionerebbero e basta.E questo è sbagliato.

I test dovrebbero funzionare solo per la ragione esatta.

Giusto per mettere qualche nome nel tuo esempio,

  • RegistryBasedDictionary implementa il dizionario dei ruoli (interfaccia).
  • RegistryBasedDictionary ha una dipendenza dal ruolo RegistryAccessor, implementato da RegistryWinAPIWrapper.

Attualmente sei interessato a testare RegistryBasedDictionary.Gli unit test inietterebbero una dipendenza fittizia per il ruolo RegistryAccessor e testerebbero l'interazione prevista con le dipendenze.

  • Il trucco qui per evitare inutili test di manutenzione è "Specificare con precisione cosa dovrebbe succedere..e niente di più." (Da il libro GOOS (da leggere per TDD dal sapore simulato), quindi se l'ordine delle chiamate al metodo di dipendenza non ha importanza, non specificarlo nel test.Ciò ti lascia libero di modificare l'ordine delle chiamate nell'implementazione.)
  • Progettare i ruoli in modo tale che non contengano perdite dalle implementazioni effettive - mantenere i ruoli indipendenti dall'implementazione.

L'unico motivo per modificare i test di RegistryBasedDictionary sarebbe un cambiamento nel comportamento di RegistryBasedDictionary e non in nessuna delle sue dipendenze.Quindi se è interazione con le sue dipendenze o il cambiamento di ruoli/contratti, i test avrebbero bisogno di essere aggiornati.Questo è il prezzo che devi pagare per i test basati sull’interazione, per avere la flessibilità di testare in modo isolato.Tuttavia, in pratica, non accade così spesso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top