L'iniezione di dipendenza è essenziale per i test unitari?


56

L'uso dell'iniezione di dipendenza (DI) è essenziale per i test unitari?

Non riesco a pensare a un'altra alternativa per isolare il codice in modo che possa essere testato. Inoltre, tutti gli esempi che ho mai visto usano questo modello. È perché è l'unica opzione praticabile o ci sono altre alternative?


5
L'iniezione di dipendenza non è essenziale, ma lo è il concetto più ampio di Inversione di controllo.
Jeremy Heiler,

C'è qualcosa da dire per la scala qui. Se ho una piccola base di codice con pochissimi livelli, DI potrebbe non essere utile.
JB King,

@JBKing se hai una piccola base di codice non hai bisogno di livelli o test unitari
Sklivvz

1
Molte decisioni di progettazione sono necessarie per rendere testabile il codice. Inizia a scrivere test e scoprilo.
Nathan Cooper,

Risposte:


43

DI semplifica notevolmente i test unitari. Ma puoi ancora scrivere unit test senza DI. Molti test unitari sono già stati scritti prima che il DI diventasse diffuso. (Certo, alcune di queste tecniche usate identiche o molto simili a DI senza sapere che ha un nome di fantasia :-)

Io stesso ho usato ad esempio interfacce e fabbriche molto prima di conoscere DI. Il nome della classe di fabbrica effettiva potrebbe essere stato letto da un file di configurazione o passato al SUT come argomento.

Un altro approccio consiste nell'utilizzare singleton (o dati accessibili a livello globale in generale). Sì, lo so che non è raccomandato da molti (incluso me stesso) in generale. Tuttavia, può essere praticabile in situazioni specifiche, specialmente se il singleton contiene dati di configurazione statici che non sono specifici del caso di prova, ma differiscono tra ambiente di produzione e di prova. Ovviamente ha i suoi problemi noti, quindi DI è superiore se puoi usarlo. Ma spesso (ad esempio nei sistemi legacy) non puoi.

A proposito, lavorare in modo efficace con il codice legacy descrive molti trucchi per ottenere il codice legacy coperto dai test. Molti di questi non sono carini e non sono intesi come una soluzione a lungo termine. Ma ti consentono di creare i primi preziosi test unitari su un sistema altrimenti non verificabile ... che ti consente di iniziare il refactoring e infine (tra gli altri) introdurre DI.


D'accordo, DI è particolarmente utile per deridere oggetti. Ma ci sono molti scenari in cui DI è inutile nei test.
Kemoda,

@Kemoda come ad esempio cosa?
BЈовић,

@VJovic Ad esempio oggetti standalone. Penso a un metodo che accetta alcuni argomenti ed esegue alcune cose ma non dipende da un altro componente (quindi non è necessario un DI).
Kemoda,

@Kemoda Sembra che tu stia descrivendo la programmazione funzionale e stai usando DI. Stai iniettando le tue dipendenze come parametri del metodo.
Erik Dietrich,

3
@Huggie, perché i dettagli di implementazione verrebbero divulgati qui? La dipendenza iniettata è in genere nascosta dietro un'interfaccia e il punto è che la classe client non ha idea di - e non si preoccupa di - se l'implementazione effettiva di questa dipendenza sia una vera classe di produzione o una simulazione. La sua istanza avviene al di fuori della classe client, arriva solo a vedere l'istanza già pronta.
Péter Török,

56

Il disaccoppiamento è essenziale per i test unitari. DI è un ottimo modo per ottenere il disaccoppiamento.


17
Un'affermazione molto vera che non risponde in alcun modo alla domanda.
Travis,

10

A seconda delle tecnologie in uso, è possibile isolare le dipendenze senza utilizzare DI. Ad esempio, nel mondo .NET, Moles consente di isolare le dipendenze senza il modello DI.

Detto questo, credo che questi quadri di isolamento siano scritti e destinati a situazioni nel tuo codice con dipendenze esterne (filesystem, database, ecc.). Cioè, il fatto che uno possa fare questo non significa che dovrebbe farlo.

L'iniezione di dipendenza consente il testing di unità, ma consente anche la modifica del comportamento di un oggetto senza alterare il codice di quell'oggetto (principio aperto / chiuso). Quindi, non è solo il codice verificabile, ma il codice flessibile che ne risulta. Ho generalmente scoperto che esiste una forte correlazione tra codice gestibile / flessibile e codice verificabile.


3
Oppure Moles ti fa pensare di avere un codice testabile pulito e ben ponderato perché stai testando attraverso la magia.
Wyatt Barnett,

@WyattBarnett Sì, molto vero. Moles ha una capacità inducente a convincere qualcuno a dire "chi ha bisogno di tutto questo polimorfismo e cose aperte / chiuse, comunque?!?"
Erik Dietrich,

1
La talpa è stata sostituita da falsi e dalla pagina falsi "Il framework Fakes aiuta gli sviluppatori a creare, mantenere e iniettare implementazioni fittizie nei loro test unitari" Mi sembra DI.
Firma il

1
@Sign Il tuo link dice anche "Il framework Fakes può essere usato per evitare qualsiasi metodo .NET, compresi i metodi non virtuali e statici in tipi sigillati". Il fatto che i framework di isolamento possano essere utilizzati insieme a DI non significa che siano DI.
Erik Dietrich,

4

No, DI non è essenziale per i test unitari, ma aiuta molto.

Puoi usare le fabbriche o i localizzatori e testare come faresti con DI (ma non altrettanto elegante e richiederebbe una maggiore configurazione).

Inoltre, Mock Objects sarebbe importante nei sistemi legacy in cui molte chiamate sono delegate a funzioni anziché a dipendenze. (Gli oggetti finti possono anche essere ampiamente utilizzati anche in una configurazione corretta)

Ci possono essere configurazioni in cui il test è quasi impossibile. Ma ciò non si basa sul fatto che venga utilizzata o meno l'iniezione di dipendenza.


3

No , l'iniezione di dipendenza non è essenziale per i test unitari.

L'iniezione di dipendenza aiuta se si dispone di una classe che necessita di un'istanza di classe dipendente per eseguire una subelaborazione. Invece di DI è possibile separare la logica di un metodo di business in una parte di raccolta dati (che non è testabile dall'unità) e una parte di calcolo che può essere testata dall'unità.

Esempio (utilizzando DI) Questa implementazione dipende da Dipendente, Account, ...

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     if (amount > 100 && employee.isStudent())
        return false;
     if (to.getOwner().getFamiliyName() == employee.getFamilyName() && ...
        return false; // cannot transfer money to himself;
     ...
 }

Dopo la separazione dei dati raccolti e il calcolo:

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     return hasPermissionToTransferMoney(employee.isStudent(), employee.getFamilyName(), to.getOwner().getFamilyName(), ...);
 }

 // the actual permission calculation
 static bool hasPermissionToTransferMoney(boolean isStudent, string employeeFamilyName, string receiverFamilyName, ...)
     if (amount > 100 && isStudent)
        return false;
     if (receiverFamilyName == employeeFamiliyName && ...
        return false; // cannot transfer money to himself
     ...
 }

La parte di calcolo può essere facilmente testata senza iniezione di dipendenza.


1
  • L'iniezione di dipendenza non è essenziale per i test unitari

  • L'inversione del controllo invece è essenziale quando si desidera scambiare un'implementazione con un'altra.


0

Sì, ci sono alternative all'uso di DI per l'isolamento.

Un'alternativa è quella di utilizzare le fabbriche o un ServiceLocator che può essere configurato dai test per restituire oggetti finti invece di quelli reali.

Un altro è usare una struttura di isolamento o uno strumento di derisione adatti. Tali strumenti esistono per quasi tutti i moderni linguaggi di programmazione (almeno per Java, C #, Ruby e Python). Possono isolare una classe in fase di test dall'implementazione di altre classi / tipi, anche quando la classe testata istanzia direttamente le sue dipendenze.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.