Domanda

Nei miei rapporti sul profiler vedo sempre più i risultati dei test basati su simulazione con iniezione di dipendenza. Molte dipendenze erano statiche, ma poiché vogliamo testare i metodi isolatamente, vengono cambiate in membri di istanza, come nell'esempio seguente:

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   // Constructor used in production 
   public ShortLivedThing() {
     dep1 = new Dep1(); dep2 = new Dep2(); dep3 = new Dep3();
   }

   // DI for testing 
   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1(); dep2 = d2(); dep3 = d3();
   }
}

A loro volta le dipendenze la maggior parte delle volte hanno altre dipendenze e così via. Ciò si traduce nella creazione di un'istanza di un albero di oggetti (principalmente "statici") ogni volta una chiamata di metodo viene eseguita al di fuori dei test. Ognuno degli oggetti è molto piccolo (solo alcuni puntatori), ma l'effetto dell'albero lo trasforma in un aumento delle prestazioni sempre crescente.

Cosa possiamo fare al riguardo?

È stato utile?

Soluzione

Mi sembra che sia necessario sfruttare le funzionalità che può offrirti un adeguato framework di iniezione delle dipendenze. Non utilizzare logiche costruttive diverse per test / produzione.

Con la primavera, le iniezioni di singleton vengono eseguite solo all'avvio del contenitore. Le iniezioni di prototipi vengono eseguite ogni volta. Il cablaggio completo viene inoltre eseguito ogni volta che si esegue un test dell'unità, se viene cablato. Pertanto, i test unitari di profilazione non sono generalmente una buona idea.

Forse stai usando troppo poco gli ambiti singleton e troppo l'ambito del prototipo? (Prototype = nuova istanza ogni volta)

La cosa bella dell'iniezione di primavera è che puoi usare i proxy dell'oscilloscopio, il che significa che il tuo grafico a oggetti può assomigliare a questo:

 A Singleton
 |
 B Singleton
 |
 C Prototype (per-invocation)
 |
 D Singleton
 |
 E Session scope (web app)
 |
 F Singleton

E ogni richiesta creerebbe solo 1 istanza di C e un'istanza di E per sessione. A, B, D e F sono singoli. Se non si tratta di un'app Web, per impostazione predefinita non si ha l'ambito della sessione, ma è anche possibile creare ambiti personalizzati (un ambito "Finestra" suona bene per un'app desktop con finestre). L'indizio qui è che puoi " introdurre " ambiti a qualsiasi livello, in modo efficace è possibile avere dieci livelli di oggetti singleton e all'improvviso viene visualizzato qualcosa nell'ambito della sessione. (Questo può davvero rivoluzionare il modo in cui implementate alcune funzionalità trasversali in un'architettura a strati ma questa è una storia diversa)

Questo dà davvero la minima creazione di oggetti possibile all'interno di un modello DI, credo.

Anche se questo è Spring for Java, credo che molti altri framework DI dovrebbero supportare funzionalità simili. Forse non i più minimalisti.

Altri suggerimenti

Penso che dovresti avere solo il costruttore "DI". Chiamate questo costruttore per i test e per la produzione.

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1; dep2 = d2; dep3 = d3;
   }
}

In questo modo non si ha il problema di creare un'istanza di un albero di oggetti ogni volta che viene effettuata una chiamata al metodo al di fuori dei test. Naturalmente, per la produzione devi collegare correttamente i tuoi oggetti all'esterno agli oggetti partecipanti stessi, il che è una buona cosa.

In sintesi: non cercare il 50% DI / 50% hardcoding, scegli il 100% DI.

Se il problema riguarda i test lenti, prova a eseguirli in parallelo e non lasciare che il processo di test interrompa i tuoi programmatori.

Automatizza questo processo:

  • Quando qualcuno fa il check in, crea un build dal repository.
  • Esegui test su questa build.
  • E - Invia i risultati allo sviluppatore che ha effettuato il check-in.

È meglio se il primo check in non viene eseguito sul repository effettivo. Passa a uno temporaneo e costruisci questo. Opzionalmente è possibile eseguire test delle prestazioni, controlli di stile ecc. E includerli nell'e-mail. In questo caso, aggiungi un passaggio al processo automatizzato:

  • Se i test superano (e soddisfano i criteri opzionali), unisci il nuovo codice con il repository effettivo.

In questo modo, i test lenti non sono un problema. Inoltre, quando uno sviluppatore deve sapere se il suo codice ha rotto qualcosa o ha aumentato le prestazioni che si aspettava, si limita a fare il check-in e attende l'e-mail generata per lei.

Che ne dici di passare i riferimenti?

Il meglio che posso inventare è mettere tutte le dipendenze in un "contesto". oggetto, che viene quindi condiviso tra tutte le istanze. Ciò dovrebbe mitigare un po 'il problema delle prestazioni.

Se stai prendendo di mira .NET, controlla Autofac . Ha vari ambiti (singleton, factory, container) per modificare gli aspetti della creazione, smaltimento deterministico per tenere a bada l'utilizzo delle risorse e consente di utilizzare GeneratedFactories ed espressioni lambda per configurare i componenti ed evitare il costo della riflessione.

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