I test unitari dovrebbero riguardare solo software "funzionale"


9

Stiamo utilizzando StructureMap in un nuovo progetto di sviluppo software. Uno dei membri del team ha implementato un test unitario che testa sostanzialmente la configurazione del contenitore StructureMap . Lo fa nel modo seguente;

  • Conta il numero di istanze di assembly configurati per le classi nel nostro spazio dei nomi dell'applicazione.
  • Definisce le istanze previste a livello di classe
  • Asserisce che le istanze previste corrispondono alle istanze totali trovate.
  • Asserisce che le istanze previste corrispondono a quelle definite nel test

Un esempio di questo è;

var repositories = container.GetAllInstances<IEnvironmentRepository>();
Assert.AreEqual(1, repositories .Count());
foundInstances = foundInstances + repositories .Count();

Abbiamo anche 'unit test' per la seguente classe;

public MyClass(IEnvironmentRepository environmentRepository)
        {

        }

In questi test, prendiamo in giro IEnvironmentRepository, quindi non lo inietteremmo dal contenitore come accadrebbe nel sistema live.

Un collega ha ignorato il test unitario sulla configurazione della strutturemap con un commento lungo la linea di "Test unitario verifica solo la propria configurazione". Questo era ovviamente lo scopo del test e secondo me è perfettamente valido. Ho chiesto al ragazzo che ha ignorato il test di rimuovere la configurazione della struttura per IEnvironmentRepository(con il test ancora ignorato) ed eseguire l'intera suite di test dell'unità, tutti hanno superato. Abbiamo quindi eseguito l'applicazione ed è caduta perché la configurazione del contenitore non era più valida. Secondo me, questo ha dimostrato il valore del test, il mio collega non era ancora d'accordo. Ha semplicemente affermato che non dovremmo testare la configurazione, ma ritengo che rientri nell'ambito di un test unitario.

Quindi una serie di domande;

  • È un test unitario valido - Stiamo testando la configurazione del nostro contenitore, non che la strutturemap funzioni (ma posso vedere la sovrapposizione)
  • In caso contrario, come è possibile convalidare la configurazione senza testarla. Come si può impedire a qualcuno di eliminare accidentalmente una riga di codice richiesta e archiviarla?
  • Il MyClasstest unitario dovrebbe risolvere l'istanza di IEnvironmentRepositorydal contenitore e passare questo?

10
9 su 10 disaccordi sui test derivano dal fatto che i framework supportano i test automatizzati in tutte le loro forme e le persone vogliono entrare in semantica sul fatto che un particolare test automatizzato sia o meno un test unitario valido e corretto . Il test che descrivi suona come il tipo di test di unità non abbastanza che può essere molto utile avere e automatizzare (ed eseguire al check-in) - semplicemente non chiamarlo un test di unità. Chiedi se il tuo collega dormirà meglio la notte se il test vivesse nella sua caratteristica / cartella che era chiaramente separata.
Jeroen Mostert,

2
Anche questa è la mia opinione, probabilmente utile, e sebbene non sia strettamente un test unitario, aggiunge valore e questo è stato dimostrato. La sua risposta fu che gli altri test di unità lo avrebbero raccolto, ma a mio avviso, se fossero stati scritti come test di unità rigorosi, verrebbero derisi le dipendenze e quindi non si saprebbe mai se la configurazione fosse valida fino a quando non la si utilizzava.
ChrisBint,

4
Il tuo collega ha ragione quando dice di non testare la configurazione, in quanto una configurazione autentica che può effettivamente variare in base alla distribuzione non può / non deve essere testata - chi può dire che "rosso" è sbagliato e "blu" non lo è? Il test sarebbe strettamente associato a una configurazione. Ma la configurazione legata agli artefatti del codice è un po 'un'eccezione, perché non varia e ci sono chiaramente modi per sbagliarli. Idealmente, tale configurazione dovrebbe essere generata in fase di compilazione dai metadati DRY, ma laddove ciò non sia possibile un test come questo aggiunge valore. Meglio di un errore di distribuzione evitabile.
Jeroen Mostert,

2
Quello che stai descrivendo non verifica un'unità, verifica la configurazione di un software di terze parti. È straordinariamente utile avere test che testano queste cose, ma sono test di integrazione, non test unitari, e la disconnessione potrebbe essere la radice del disaccordo.
Phoshi,

3
@ChrisBint Bontà gentile no, ho scritto un sacco di test container da solo. Hanno molto valore, semplicemente non sono test unitari. Va bene, i test di integrazione sono estremamente preziosi per la cattura di cose che i test unitari non possono fare .
Phoshi,

Risposte:


13

Questo è un test automatico perfettamente valido da avere. Li chiamo "test di architettura" in quanto verificano la solidità dei componenti scheletrici della tua base di codice.

Il contenitore IoC è in grado di risolvere e comporre tutti gli alberi degli oggetti nell'applicazione? Auto Mapper può mappare tra tutti i suoi oggetti registrati senza errori? Il livello centrale in un'architettura di cipolla non fa riferimento a qualcosa di esterno?

Questi test possono farti risparmiare molto tempo quando si verifica un bug di configurazione, indicando l'esatto colpevole. Buoni framework ti daranno messaggi di errore molto precisi su cosa c'è che non va e li ricevi non appena esegui i test (idealmente, continuamente) invece di seppellire in profondità una traccia dello stack di runtime se sei fortunato.

Che si tratti di unit test ... probabilmente no, ma funzionano ancora in memoria per la maggior parte e funzionano abbastanza velocemente. Inoltre, non lo so, non è che esistesse una definizione universalmente accettata di unit test.


Ironia della sorte, questo è praticamente il modo in cui l'ho spiegato al mio collega e anche con la convalida (eliminare una delle istanze del contenitore ed eseguire l'applicazione) non vedeva ancora alcun valore. Capisco che ognuno abbia la propria opinione e ho espresso la mia;) Adoro il termine "test di architettura", lo ruberò!
ChrisBint,

6

Il problema con un test come questo che verifica gli interni del programma, anziché un suo requisito. È che il test può fallire anche se il programma funziona come richiesto.

Nel tuo caso, ogni volta che cambi l'impostazione del contenitore, forse hai una nuova dipendenza che deve essere iniettata, rompi il test.

Inoltre, se si aggiunge il requisito di dipendenza extra, ma si dimentica di aggiungerlo al contenitore e modificare il test del contenitore. tutto passerà, ma il tuo programma andrà in crash.

Un test automatizzato migliore sarebbe quello di avviare il programma e vedere se si blocca.

È necessario rilevare questi tipi di errore durante l'integrazione o i test dell'interfaccia utente anche se rientrano nei test unitari.

Detto questo, la crescente complessità della configurazione del contenitore è un dolore nel culo. Forse ne valgono alcuni "cattivi".


1

Codice test unit test. Qualunque cosa al di fuori di questo è un "altro" test automatico: chiamalo come vuoi. Sembra che tu stia testando la configurazione qui. Se la configurazione può cambiare a seconda dell'ambiente, non appartiene a un test unitario. Prendere in considerazione l'aggiunta di un attributo test per indicare che il test è di tipo diverso rispetto agli altri test.


La configurazione è statica, non è guidata dall'ambiente, tutte le classi esistenti nella configurazione verranno utilizzate su tutti gli ambienti allo stesso modo. Sì, il numero di istanze che potrebbero essere presenti nella configurazione dovrebbe corrispondere al numero di istanze presenti nella configurazione, che fa parte del test. Come mostrato dal mio esempio, la rimozione di IEnvironmentRepository ha permesso il superamento dei test delle altre unità. Il test contenitore specifico non avrebbe avuto esito positivo su 2 assert; 1 - Il numero totale di possibili dichiarazioni di istanza non corrispondeva e 2 - il numero specifico di istanze di IEnvironmentRepository non corrisponderebbe.
ChrisBint,

1
La correttezza del contenitore è definita dal programmatore. Il fatto che sia il codice in prova che il test stesso debbano cambiare per ogni modifica fa immediatamente suonare le campane di allarme. DI è un mezzo per un fine e non il fine in sé. È perfettamente possibile scrivere codice in stile DI senza strutturemap ergo, a mio avviso non è un test di unità in buona fede. Il contenitore ovviamente deve essere provato, ma l'efficacia di farlo con i test automatizzati sembrerebbe in qualche modo discutibile con le informazioni limitate fornite qui.
Robbie Dee,

2
I test unitari hanno richiesto 10 minuti per bussare. La distribuzione potrebbe richiedere più di un'ora.
ChrisBint,

1
Dato che parte del test unitario convalida specificamente l'esistenza di una singola linea nella configurazione, non sono sicuro di come ciò non avrebbe potuto essere più isolato. Il conteggio generale con cui potrei essere d'accordo.
ChrisBint,

1
Ci potrebbe essere un po 'di chilometraggio in loro allora - una richiesta di giudizio per te davvero. Ma dovrebbero essere separati fisicamente o tramite qualche attributo.
Robbie Dee,

0

La responsabilità del contenitore di iniezione delle dipendenze è di incollare diversi moduli in un'unica applicazione funzionante .

Se scrivi test automatici per la tua applicazione, dovresti avere pochi "test di integrazione (o accettazione) che eseguono test" da un capo all'altro ", che testeranno l'intera pipeline della tua applicazione, che tutti i moduli coinvolti in un particolare test case saranno incollati insieme correttamente .

Pertanto, tali test di integrazione falliranno se il contenitore di iniezione delle dipendenze non è stato configurato correttamente. Ciò rende inutili i test unitari per il contenitore stesso, poiché il test di integrazione dovrebbe mostrare possibili errori nella configurazione del contenitore.

Non è necessario coprire tutti i possibili casi di test nei test di integrazione, ma solo un caso di test per funzionalità che copre l'intero percorso dall'interfaccia utente al database.

Se i casi di test di integrazione non coprono l'istanza di una particolare dipendenza, è sufficiente aggiungerne una.

Con i test di integrazione è possibile cambiare liberamente i contenitori senza riscrivere i test unitari per la loro configurazione.


0

IMO, le risposte sono:

  1. È un test unitario valido - Stiamo testando la configurazione del nostro contenitore, non che la strutturemap funzioni (ma posso vedere la sovrapposizione)

    • È un unit test valido per la strutturemap , non per il tuo progetto, perché un test unitario verifica un codice specifico, deridendo tutte le dipendenze se necessario per testare la logica implementata. La logica di configurazione è implementata all'interno di Structuremap, quindi questa libreria deve essere ben testata e deve contenere unit test come questo che hai citato, e altro: dovrebbe contenere centinaia di test come questo, deridendo dinamicamente più configurazioni in fase di esecuzione e test per vedere se il il contenitore si comporta come dovrebbe.
  2. In caso contrario, come è possibile convalidare la configurazione senza testarla. Come si può impedire a qualcuno di eliminare accidentalmente una riga di codice richiesta e archiviarla?

    • Puoi testare manualmente la configurazione nell'ambiente necessario e puoi anche creare un'automazione per questo (test automatizzato), che verifica la configurazione specifica di cui hai bisogno (non c'è bisogno di deridere roba in fase di esecuzione).
  3. Il test dell'unità MyClass dovrebbe risolvere l'istanza di IEnvironmentRepository dal contenitore e passarlo?

    • No, questo è un test unitario perfetto, perché deridi la dipendenza e testa la logica di MyClass in modo isolato.

-1

UnitTest verifica il comportamento desiderato di un'unità in separazione .

Ciò significa che qualsiasi tipo di configurazione non rientra nell'ambito di UnitTest .

Tuttavia dovresti avere dei test automatici per le tue configurazioni, ma questi non sono UnitTest ...


Dove stai ottenendo la definizione di unità?
ChrisBint,

Mi piace quello di Roy Osherove in The Art Of Unittesting : A Unit è un pezzo di codice (di produzione) che ha lo stesso motivo per cambiare. Nel mio mondo questo di solito va da una singola classe a tre o cinque ...
Timothy Truckle

Questo è il codice di produzione che viene testato.
ChrisBint,

Volevo solo distrarre i nitpicker , non aspettandomi che funzionasse anche al contrario ...; o)
Timothy Truckle,
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.