2009-08-11 11 views
5

Mi chiedo quale sia la procedura migliore per ricablare i binding in un kernel.Ignora l'override del binding del kernel

Ho una classe con un kernel e un modulo di classe privata con i binding di produzione predefiniti.

Per i test voglio sovrascrivere questi binding in modo da poter scambiare gli oggetti Double Doubles/Mocks.

fa

MyClass.Kernel.Load(new InlineModule(m=> m.Bind<IDepend>().To<TestDoubleDepend>())) 

di override eventuali attacchi esistenti per IDepend?

risposta

0

Vorrei aggiungere un costruttore a MyClass che accetta un modulo.
Questo non verrebbe utilizzato nella produzione ma verrebbe utilizzato nel test.
Nel codice di test passerei un modulo che ha definito i duplicati richiesti.

1

Cosa tendo a fare è di avere un progetto di test separata completa con il proprio attacchi - Sono ovviamente partendo dal presupposto che stiamo parlando di unit test di qualche tipo. Il progetto di test usa il proprio kernel e carica il modulo nel progetto di test in quel kernel. I test nel progetto vengono eseguiti durante le build CI e per le build complete eseguite da uno script di build, sebbene i test non vengano mai distribuiti in produzione.

Mi rendo conto che la configurazione del progetto/soluzione potrebbe non consentire questo tipo di organizzazione, ma sembra essere piuttosto tipica da quello che ho visto.

5

Cerco di utilizzare il kernel DI direttamente nel mio codice il meno possibile, facendo invece affidamento sull'iniezione del costruttore (o proprietà in casi selezionati, come le classi Attribute). Dove devo, tuttavia, utilizzo un livello di astrazione, in modo da poter impostare l'oggetto del kernel DI, rendendolo mockable nei test unitari.

Ad esempio:

public interface IDependencyResolver : IDisposable 
{ 
    T GetImplementationOf<T>(); 
} 

public static class DependencyResolver 
{ 
    private static IDependencyResolver s_resolver; 

    public static T GetImplementationOf<T>() 
    { 
     return s_resolver.GetImplementationOf<T>(); 
    } 

    public static void RegisterResolver(IDependencyResolver resolver) 
    { 
     s_resolver = resolver; 
    } 

    public static void DisposeResolver() 
    { 
     s_resolver.Dispose(); 
    } 
} 

Utilizzando un modello come questo, è possibile impostare la IDependencyResolver dal test di unità chiamando RegisterResolver con un'implementazione finto o falso che restituisce qualsiasi oggetto che si desidera, senza dover cablare moduli pieni . Ha anche un vantaggio secondario di astrarre il codice da un particolare contenitore IoC, se si sceglie di passare a uno diverso in futuro.

Naturalmente, vorresti anche aggiungere ulteriori metodi a IDependencyResolver come le tue esigenze dettano, sto solo includendo le basi qui come esempio. Sì, questo richiederebbe quindi di scrivere un wrapper super semplice attorno al kernel di Ninject che implementa anche IDependencyResolver.

Il motivo per cui si desidera eseguire questa operazione è che i test di unità devono solo testare una cosa e utilizzando il proprio contenitore IoC, si sta davvero esercitando più della classe in prova, che può portare a falsi negativi che rendono i test fragili e (ancora più importante) scuotono la fiducia degli sviluppatori nella loro accuratezza nel tempo. Questo può portare a testare l'apatia e l'abbandono poiché diventa possibile che i test falliscano ma il software funzioni ancora correttamente ("non preoccuparti, quello fallisce sempre, non è un grosso problema").

+0

+1 per il modello resolver, tuttavia, VERAMENTE, provare a utilizzare l'iniezione del costruttore in MyClass che richiede IDepend. in questo modo, i tuoi test unitari non hanno nemmeno bisogno del tuo contenitore IoC. –

+0

Perché non fare qualcosa di più semplice come eseguire l'override dei binding in questo modo ... var kernel = new StandardKernel (new ProductionModule (config)); kernel.Rebind () .To () .InSingletonScope(); –

+0

È possibile sovrascrivere i collegamenti, ma i test eseguono anche il test del contenitore DI, insieme all'unità sottoposta a test. In alcune situazioni, questo non è un grosso problema ma può portare a falsi negativi ed erodere la fede nella suite di test come delineato nella mia risposta. – krohrbaugh

1

approccio di Peter Mayer shuoul essere utile per unità di test, ma secondo me, non è più facile iniettare solo manualmente un Mock utilizzando un iniezione costruttore/proprietà?

Mi sembra che l'utilizzo di binding specifici per un progetto di test sia più utile per altri tipi di test (integrazione, funzionale) ma anche in questo caso è sicuramente necessario modificare i binding in base al test.

Il mio approccio è una sorta di mix di kronhrbaugh e Hamish Smith, che crea un "resolver di dipendenze" in cui è possibile registrare e annullare la registrazione dei moduli da utilizzare.

+0

Sono davvero d'accordo con te. Nella maggior parte dei miei test di unità, sto facendo un'iniezione "manuale" - che sembra troppo elaborata per passare semplicemente una simulazione a un costruttore! Ho usato DI in unit test in alcuni casi isolati, specifici, ma certamente una minoranza di esempi. –

0

Per un progetto su cui sto lavorando, ho creato moduli separati per ogni ambiente (test, sviluppo, stage, produzione, ecc.). Quei moduli definiscono i binding.

Poiché dev, stage e produzione utilizzano tutti gli stessi binding, ho creato un modulo comune da cui derivano tutti. Ognuno aggiunge quindi binding specifici per l'ambiente.

Ho anche un KernelFactory, che quando viene consegnato un token di ambiente spooling un IKernel con i moduli appropriati.

Questo mi consente di cambiare il mio token di ambiente che a sua volta cambierà automaticamente tutti i miei binding.

Ma se questo è per il collaudo di unità, sono d'accordo con i commenti di cui sopra che un semplice costruttore che consente l'associazione manuale è la strada da percorrere in quanto mantiene Ninject fuori dai test.

3

Sto solo sperando qualcosa come questo funziona

 var kernel = new StandardKernel(new ProductionModule(config)); 
     kernel.Rebind<ITimer>().To<TimerImpl>().InSingletonScope(); 

dove ProductionModule è il mio attacchi di produzione e sovrascrivo chiamando Rebind nello specifico caso di test. Chiamo il rebind sui pochi elementi che riassumo.

VANTAGGIO: se qualcuno aggiunge nuove associazioni al modulo di produzione, le eredito in modo che non si rompa in questo modo che può essere bello. Tutto questo funziona in Guice in Java ... sperando che funzioni anche qui.

Problemi correlati