2009-05-04 6 views
6

Sto provando ad apprendere l'iniezione di dipendenza e ho riscontrato un problema durante il test dell'unità dell'applicazione.Risoluzione dell'iniezione delle dipendenze e test delle unità

Sto scrivendo un'applicazione console e il contenitore viene creata e inizializzata in Main(), è disponibile come get-property in Program.Container, in modo da nessuna parte nella mia richiesta mi può chiamare Program.Container.Resolve<..>().

Ho una classe ServiceValidator come questo:

public class ServiceValidator 
{ 
    private readonly IConfiguration _configuration; 
    private readonly IService _service; 

    public ServiceValidator(IConfiguration configuration, IService service) 
    { 
     _configuration = configuration; 
     _service = service; 
    } 

In un'altra classe che uso

ServiceValidator serviceValidator = Program.Container.Resolve<ServiceValidator>(); 
serviceValidator.VerifyVersion(); 

E 'la chiamata a Program.Container.Resolve che mi provoca problemi nel test di unità, in quanto non ha stato configurato

È una cattiva pratica, chiamare la risoluzione sul container? Potrei creare l'istanza ServiceValidator in Main() e passare l'oggetto in giro, ma ciò sembra stupido poiché causerebbe molti parametri per gli oggetti che sono passati al metodo successivo.

Quindi penso che sia accettabile chiamare Resolve all'interno di una classe, ma il contenitore deve essere configurato per il test dell'unità. Come dovrei farlo, dovrei spostare il container in un altro posto rispetto alla classe Program? Cosa raccomanderesti?

Se è importante, sto usando l'Unità e la C#

Grazie :-)

risposta

8

È una cattiva pratica, chiamare la risoluzione sul container?Potrei creare l'istanza ServiceValidator in Main() e passare l'oggetto, ma ciò sembra stupido poiché causerebbe molti parametri per gli oggetti che sono passati al metodo successivo.

Quando si utilizza l'iniezione di dipendenza fino in fondo, non sarà necessario passare molti parametri agli oggetti. Il costruttore di ogni oggetto dovrebbe avere come parametri solo le dipendenze che esso stesso usa direttamente - non conoscerà le dipendenze transitive delle sue dipendenze dirette.

Quindi, se si dispone di una classe X che richiede un ServiceValidator, allora classe X avrà un parametro del costruttore di tipo ServiceValidator. Poi, se qualche classe Y utilizza classe X, allora la classe Y avrà un parametro del costruttore di tipo X. Si noti che Y non sa nulla circa ServiceValidator, quindi non è necessario per passare il ServiceValidator da una classe ad un'altra - l'unico posto dove è usato è quando costruisci X, e questo è spesso fatto da un framework DI o in un solo posto in una fabbrica scritta a mano.

Alcuni link per ulteriori informazioni:

+0

> Quando si utilizza l'iniezione di dipendenza fino in fondo, >, non sarà necessario passare molti parametri agli oggetti. Non ho mai considerato di fare DI per tutte le classi. Volevo solo fare DI per le classi che usano cose esterne, come servizi web, database, forse configurazione. Se capisco che hai ragione, non chiameresti mai risolutivo o nuovo su qualcosa che non sia un corso di base? Grazie per la risposta :-) – Karsten

+0

È venuto a pensare ad un'altra cosa. Significa che tutti gli oggetti sono creati in anticipo, molto prima che possano essere effettivamente necessari, forse non sono nemmeno necessari? – Karsten

+0

Quando si utilizza completamente il DI, si esegue il bootstrap dell'applicazione recuperando l'oggetto radice dell'applicazione dal contenitore DI e, successivamente, non si accede più direttamente al contenitore. http://docs.google.com/Doc?id=dd2fhx4z_5df5hw8#xefn Gli oggetti verranno creati quando viene creato un oggetto che li ha come dipendenza. In generale, tutti gli oggetti con lo stesso ambito del ciclo di vita vengono istanziati contemporaneamente. Se alcuni oggetti hanno un ambito di vita diverso, è possibile iniettare una fabbrica per istanziarli al momento giusto. –

1

Io di solito consentono chiamate a risolvere le dipendenze dal contenitore in luoghi come la principale, anche se cerco ancora di tenerli a una minimo. Quello che faccio allora è configurare il contenitore in un metodo di inizializzazione di una classe di test. L'ho inizializzato con false implementazioni per qualsiasi classe di test che ha bisogno di chiamare il contenitore.

Le classi di test che non chiamano nulla che richiedono l'inizializzazione del contenitore possono quindi ignorarlo e non utilizzare i falsi. Di solito uso i mock in questi casi.

Io uso anche il Microsoft Service Locator in modo che la dipendenza che sto prendendo è su qualcosa da .NET Framework anziché su un contenitore specifico. Questo mi consente di usare tutto ciò che voglio anche in un contenitore fatto in casa.

+0

mi piace ancora di vedere un bel modello su come risolvere questo senza fare una dipendenza dalla classe programma ... –

+0

> Continuo chiamate genere .. vuoi dire 'determinazione' chiamate al contenitore ? – Karsten

+0

Sì, voglio dire "risolvere" le chiamate al contenitore. –

0

È possibile utilizzare una classe statica come inizializzatore per il contenitore. Qualcosa come BootStrapper.cs andrebbe bene. È quindi possibile fare riferimento ai metodi di classe sia nel codice che nei test.

0

Bene, quello che stai facendo tecnicamente è un servizio nella tua classe.

Ricordo di aver letto questo articolo un po 'indietro:

http://martinfowler.com/articles/injection.html

Per le mie classi ho mai cercare di utilizzare Resolve in loro. Creo gli oggetti attraverso il contenitore quando ne ho bisogno. Per il test delle unità, uso una libreria di simulazioni e classi di stub.

0

Il problema sta nel Infatti, stai provando a testare il metodo Main. Questo metodo è praticamente impossibile per il test dell'unità.

direi che è meglio non per unità di testare il metodo principale causa:

  • L'enfasi della moderna unit testing è sulla struttura
  • Si dovrebbe ridurre al minimo la dipendenza della configurazione in test di unità. La configurazione può essere testata con test di fumo o integrazione.
+0

No, non provo a testare il metodo Main. – Karsten