Come si rilevano i problemi di dipendenza con i test unitari quando si usano oggetti finti?


98

Hai una classe X e scrivi alcuni unit test che verificano il comportamento X1. C'è anche la classe A che prende X come dipendenza.

Quando scrivi test unitari per A, deridi X. In altre parole, mentre test unitario A, imposti (postulato) il comportamento di derisione di X su X1. Il tempo passa, le persone usano il sistema, hanno bisogno di cambiamenti, X si evolve: si modifica X per mostrare il comportamento X2. Ovviamente, i test unitari per X falliranno e dovrai adattarli.

Ma cosa succede con A? I test unitari per A non falliranno quando il comportamento di X viene modificato (a causa della derisione di X). Come rilevare che il risultato di A sarà diverso quando eseguito con la X "reale" (modificata)?

Mi aspetto risposte sulla falsariga di: "Non è questo lo scopo del test unitario", ma che valore ha allora il test unitario? Ti dice davvero solo che quando tutti i test passano, non hai introdotto un cambiamento sostanziale? E quando il comportamento di alcune classi cambia (volontariamente o involontariamente), come puoi rilevare (preferibilmente in modo automatizzato) tutte le conseguenze? Non dovremmo concentrarci maggiormente sui test di integrazione?



36
Oltre a tutte le risposte suggerite, devo dire che metto in dubbio la seguente affermazione: "Ti dice davvero solo che quando tutti i test passano, non hai introdotto un cambiamento sostanziale?" Se pensi davvero che rimuovere la paura del refactoring sia di scarso valore, sei sulla buona strada verso la scrittura di codice non
mantenibile

5
Il test unitario ti dice se la tua unità di codice si comporta come previsto. Non più o meno. I falsi e i doppi di prova forniscono un ambiente artificiale e controllato per esercitare la tua unità di codice (in isolamento) per vedere se soddisfa le tue aspettative. Non più o meno.
Robert Harvey,

2
Credo che la tua premessa sia errata. Quando dici X1che stai dicendo che Ximplementa l'interfaccia X1. Se cambi l'interfaccia X1al X2mock che hai usato negli altri test, non dovresti più compilare, quindi sei costretto a correggere anche quei test. I cambiamenti nel comportamento della classe non dovrebbero avere importanza. In effetti, la tua classe Anon dovrebbe dipendere dai dettagli di implementazione (che è ciò che cambieresti in quel caso). Quindi i test unitari Asono ancora corretti e ti dicono che Afunziona data un'implementazione ideale dell'interfaccia.
Bakuriu,

5
Non so te, ma quando dovrò lavorare su una base di codice senza test, ho paura a morte che romperò qualcosa. E perché? Perché succede così spesso che qualcosa si rompe quando non era previsto. E benedici i cuori dei nostri tester, non possono testare tutto. O anche vicino. Ma un test unitario si allontanerà felicemente attraverso una routine noiosa dopo una routine noiosa.
corsiKa

Risposte:


125

Quando scrivi test unitari per A, prendi in giro X

Fai? No, a meno che non sia assolutamente necessario. Devo se:

  1. X è lento o
  2. X ha effetti collaterali

Se nessuno di questi si applica, anche i test delle mie unità lo Afaranno X. Fare qualsiasi altra cosa significherebbe condurre test di isolamento ad un estremo illogico.

Se hai parti del tuo codice usando simulazioni di altre parti del tuo codice, allora sarei d'accordo: qual è il punto di tali test unitari? Quindi non farlo. Lascia che quei test utilizzino le dipendenze reali in quanto formano test molto più preziosi in quel modo.

E se alcune persone si arrabbiano quando chiami questi test, "test unitari", chiamali semplicemente "test automatici" e continua a scrivere buoni test automatici.


94
@Laiv, No, si suppone che i test unitari fungano da unità, vale a dire eseguiti separatamente, da altri test . Nodi e grafici possono fare un'escursione. Se riesco a eseguire un test end-to-end isolato e senza effetti collaterali in breve tempo, è un test unitario. Se non ti piace quella definizione, chiamala un test automatico e smetti di scrivere test di merda per adattarti alla semantica sciocca.
David Arno,

9
@DavidArno C'è purtroppo una definizione molto ampia di isolato. Alcuni vorrebbero che un '"unità" includesse il livello intermedio e il database. Riescono a credere a ciò che vogliono, ma è probabile che su uno sviluppo di qualsiasi dimensione, le ruote si stacchino in un ordine abbastanza breve mentre il direttore della costruzione lo getterà via. In generale, se sono isolati dall'assieme (o equivalente), va bene. NB se si codifica per le interfacce, è molto più semplice aggiungere beffe e DI in seguito.
Robbie Dee,

13
Stai sostenendo un diverso tipo di test piuttosto che rispondere alla domanda. Il che è un punto valido, ma questo è un modo abbastanza subdolo di farlo.
Phil Frost,

17
@PhilFrost, per citare me stesso, " E se alcune persone si arrabbiano quando chiami questi test," test unitari ", chiamali semplicemente" test automatici "e vai avanti con la scrittura di buoni test automatizzati. " Scrivi test utili, non test sciocchi che incontra semplicemente una definizione casuale di una parola. O in alternativa, accetta che forse hai sbagliato la tua definizione di "unit test" e che hai finito di usare le beffe perché hai sbagliato. Ad ogni modo, finirai con test migliori.
David Arno,

30
Sono con @DavidArno su questo; la mia strategia di test è cambiata dopo aver visto questo discorso di Ian Cooper: vimeo.com/68375232 . Per ridurlo a una sola frase: non testare le lezioni . Comportamenti di test . I test non devono avere conoscenza delle classi / metodi interni utilizzati per implementare il comportamento desiderato; dovrebbero conoscere solo la superficie pubblica della tua API / libreria e dovrebbero testarla. Se i test hanno troppe conoscenze, stai testando i dettagli dell'implementazione e i tuoi test diventano fragili, abbinati all'implementazione e in realtà solo un'ancora intorno al collo.
Richiban,

79

Hai bisogno di entrambi. Test di unità per verificare il comportamento di ciascuna delle unità e alcuni test di integrazione per assicurarsi che siano collegati correttamente. Il problema di basarsi solo sui test di integrazione è l'esplosione combinatoria risultante dalle interazioni tra tutte le unità.

Supponiamo che tu abbia la classe A, che richiede 10 test unitari per coprire completamente tutti i percorsi. Quindi hai un'altra classe B, che richiede anche 10 unit test per coprire tutti i percorsi che il codice può percorrere attraverso di essa. Ora diciamo nella tua applicazione, devi inserire l'output di A in B. Ora il tuo codice può prendere 100 percorsi diversi dall'input di A all'output di B.

Con i test unitari, sono necessari solo 20 test unitari + 1 test di integrazione per coprire completamente tutti i casi.

Con i test di integrazione, avrai bisogno di 100 test per coprire tutti i percorsi del codice.

Ecco un ottimo video sugli svantaggi dell'affidarsi ai test di integrazione: solo i test integrati di JB Rainsberger sono una truffa HD


1
Sono sicuro che non è un caso che il punto interrogativo sull'efficacia dei test di integrazione sia andato di pari passo con i test unitari che coprono più strati.
Robbie Dee,

16
Giusto, ma da nessuna parte i tuoi test di 20 unità richiedono derisione. Se hai i tuoi 10 test di A che coprono tutti A e i tuoi 10 test che coprono tutti B e ripeti il ​​25% di A come bonus, questo sembra tra "bene" e una buona cosa. Prendere in giro i test di A in Bs sembra attivamente stupido (a meno che non ci sia davvero una ragione per cui A è un problema, ad esempio è il database o porta in una lunga rete di altre cose)
Richard Tingle

9
Non sono d'accordo con l'idea che un singolo test di integrazione sia sufficiente se si desidera una copertura completa. Le reazioni di B a quali uscite A varieranno in base all'uscita; se la modifica di un parametro in A modifica il suo output, B potrebbe non gestirlo correttamente.
Matthieu M.

3
@ Eternal21: il mio punto era che a volte il problema non era nel comportamento individuale, ma in un'interazione inaspettata. Cioè, quando la colla tra A e B si comporta inaspettatamente in alcuni scenari. Quindi sia A che B agiscono secondo le specifiche, e il caso felice funziona, ma su alcuni input c'è un bug nel codice colla ...
Matthieu M.

1
@MatthieuM. Direi che questo va oltre lo scopo di Unit Testing. Il codice della colla può essere testato autonomamente mentre le interazioni tra A e B tramite il codice della colla sono un test di integrazione. Quando vengono rilevati casi limite specifici o bug, questi possono essere aggiunti ai test delle unità di codice colla e infine verificati nel test di integrazione.
Andrew T Finnell,

72

Quando scrivi test unitari per A, deridi X. In altre parole, mentre test unitario A, imposti (postulato) il comportamento di derisione di X su X1. Il tempo passa, le persone usano il sistema, hanno bisogno di cambiamenti, X si evolve: si modifica X per mostrare il comportamento X2. Ovviamente, i test unitari per X falliranno e dovrai adattarli.

Woah, aspetta un momento. Le implicazioni dei test per il fallimento di X sono troppo importanti per passare in rassegna in quel modo.

Se la modifica dell'implementazione di X da X1 a X2 interrompe i test unitari per X, ciò indica che hai apportato una modifica all'indietro incompatibile con il contratto X.

X2 non è una X, nel senso di Liskov , quindi dovresti pensare ad altri modi per soddisfare le esigenze dei tuoi stakeholder (come l'introduzione di una nuova specifica Y, che è implementata da X2).

Per approfondimenti, vedi Pieter Hinjens: The End of Software Versions o Rich Hickey Simple Made Easy .

Dal punto di vista di A, c'è una condizione preliminare che il collaboratore rispetti il ​​contratto X. E la tua osservazione è effettivamente che il test isolato per A non ti dà alcuna garanzia che A riconosca i collaboratori che violano il contratto X.

Revisionare i test integrati sono una truffa ; ad alto livello, ci si aspetta che tu abbia tutti i test isolati di cui hai bisogno per assicurarti che X2 attui correttamente il contratto X, e tutti i test isolati di cui hai bisogno per assicurarti che A faccia la cosa giusta dato risposte interessanti da una X, e un numero inferiore di test integrati per garantire che X2 e A concordino sul significato di X.

A volte vedrai questa distinzione espressa come test solitari vs sociabletest; vedere Jay Fields che lavora in modo efficace con i test unitari .

Non dovremmo concentrarci maggiormente sui test di integrazione?

Ancora una volta, vedere i test integrati sono una truffa - Rainsberger descrive in dettaglio un circuito di feedback positivo che è comune (nelle sue esperienze) ai progetti che si basano su test integrati (spelling delle note). In sintesi, senza i test isolati / solitari che esercitano pressione sul progetto , la qualità si riduce, portando a più errori e test più integrati ....

Avrai anche bisogno di (alcuni) test di integrazione. Oltre alla complessità introdotta da più moduli, l'esecuzione di questi test tende ad avere più resistenza rispetto ai test isolati; è più efficiente eseguire iterazioni su controlli molto veloci quando il lavoro è in corso, salvando i controlli aggiuntivi per quando pensi di aver terminato.


8
Questa dovrebbe essere la risposta accettata. La domanda delinea una situazione in cui il comportamento di una classe è stato modificato in modo incompatibile, ma ancora esteriormente sembra identico. Il problema qui sta nella progettazione dell'applicazione, non nei test unitari. Il modo in cui questa circostanza verrebbe colta nei test sta usando un test di integrazione tra queste due classi.
Nick Coad,

1
"senza i test isolati / solitari che esercitano pressione sul progetto, la qualità si degrada". Penso che questo sia un punto importante. Oltre ai controlli comportamentali, i test unitari hanno l'effetto collaterale di costringerti ad avere un design più modulare.
MickaëlG,

Suppongo che sia tutto vero, ma come può aiutarmi se una dipendenza esterna introduce una modifica all'indietro incompatibile con il contratto X? Forse una classe che esegue I / O in una libreria rompe la compatibilità e stiamo deridendo X perché non vogliamo che i test unitari in CI dipendano da I / O pesanti. Penso che l'OP stia chiedendo di provare questo, e non capisco come questo risponda alla domanda. Come testare questo?
Gerrit,

15

Vorrei iniziare dicendo che la premessa fondamentale della domanda è errata.

Non stai mai testando (o deridendo) le implementazioni, stai testando (e deridendo) le interfacce .

Se ho una vera classe X che implementa l'interfaccia X1, posso scrivere un XM finto che è anche conforme a X1. Quindi la mia classe A deve usare qualcosa che implementa X1, che può essere di classe X o finta XM.

Supponiamo ora di cambiare X per implementare una nuova interfaccia X2. Bene, ovviamente il mio codice non viene più compilato. A richiede qualcosa che implementa X1 e che non esiste più. Il problema è stato identificato e può essere risolto.

Supponiamo invece di sostituire X1, lo modifichiamo e basta. Ora la classe A è pronta. Tuttavia, il finto XM non implementa più l'interfaccia X1. Il problema è stato identificato e può essere risolto.


L'intera base per il unit testing e il derisione è che si scrive codice che utilizza interfacce. Un consumatore di un'interfaccia non importa come viene attuato il codice, solo che il medesimo contratto è rispettata (ingressi / uscite).

Questo si interrompe quando i tuoi metodi hanno effetti collaterali, ma penso che possa essere tranquillamente escluso in quanto "non può essere testato o deriso dall'unità".


11
Questa risposta fa molte ipotesi che non devono valere. In primo luogo, si assume, approssimativamente, che siamo in C # o Java (o, più precisamente, che siamo in un linguaggio compilato, che il linguaggio abbia interfacce e che X implementi un'interfaccia; nessuna di queste deve essere vera). In secondo luogo, presuppone che qualsiasi modifica al comportamento o al "contratto" di X richieda una modifica all'interfaccia (come intesa dal compilatore) implementata da X. Questo è chiaramente non è vero, anche se ci troviamo in Java o C #; puoi cambiare un'implementazione del metodo senza cambiarne la firma.
Mark Amery,

6
@MarkAmery È vero che la terminologia "interfaccia" è più specifica per C # o Java, ma penso che il punto stia assumendo un "contratto" definito di comportamento (e se questo non è codificato, rilevarlo automaticamente è impossibile). Hai anche ragione nel dire che un'implementazione può essere modificata senza una modifica al contratto. Ma una modifica all'implementazione senza una modifica dell'interfaccia (o del contratto) non dovrebbe influire su alcun consumatore. Se il comportamento di A dipende da come viene implementata l'interfaccia (o il contratto), è impossibile (significativamente) test unitario.
Vlad274,

1
"Hai anche ragione nel dire che un'implementazione può essere modificata senza una modifica al contratto" - anche se è vero, non è questo il punto che stavo cercando di fare. Piuttosto, sto affermando una distinzione tra il contratto (la comprensione da parte di un programmatore di ciò che un oggetto dovrebbe fare, forse specificato nella documentazione) e l' interfaccia (un elenco di firme del metodo, compreso dal compilatore) e dicendo che il contratto può essere modificato senza cambiare l'interfaccia. Tutte le funzioni con la stessa firma sono intercambiabili dal punto di vista del sistema di tipi, ma non nella realtà!
Mark Amery,

4
@MarkAmery: non penso che Vlad stia usando la parola "interfaccia" nello stesso senso in cui la usi; il modo in cui leggo la risposta, non sta parlando di interfacce in senso stretto C # / Java (cioè un insieme di firme di metodi) ma nel senso generale della parola, come ad esempio usato nei termini "interfaccia di programmazione dell'applicazione" o addirittura " interfaccia utente". [...]
Ilmari Karonen,

6
@IlmariKaronen Se Vlad sta usando "interfaccia" per indicare "contratto" anziché nel senso stretto di C # / Java, allora la frase "Ora, supponiamo di cambiare X per implementare una nuova interfaccia X2. Beh, ovviamente il mio codice non si compila più. " è semplicemente falso, poiché è possibile modificare un contratto senza modificare le firme dei metodi. Ma onestamente, penso che il problema qui sia che Vlad non sta usando entrambi i significati in modo coerente, ma li sta confondendo , il che è ciò che conduce lungo il percorso di affermare che qualsiasi modifica al contratto X1 causerà necessariamente un errore di compilazione senza notare che è falso .
Mark Amery,

9

Rispondere alle tue domande a turno:

quale valore ha quindi il test unitario

Sono economici da scrivere ed eseguire e ricevi feedback in anticipo. Se rompi X, scoprirai più o meno immediatamente se hai buoni test. Non considerare nemmeno di scrivere test di integrazione a meno che tu non abbia testato tutti i tuoi livelli (sì, anche sul database).

Ti dice davvero solo che quando tutti i test passano, non hai introdotto un cambiamento sostanziale

Avere test che superano potrebbe effettivamente dirti molto poco. Potresti non aver scritto abbastanza test. Potresti non aver testato abbastanza scenari. La copertura del codice può aiutare qui, ma non è un proiettile d'argento. Potresti avere dei test che passano sempre . Quindi il rosso è il primo passo spesso trascurato di rosso, verde, refattore.

E quando il comportamento di alcune classi cambia (volontariamente o involontariamente), come puoi rilevare (preferibilmente in modo automatizzato) tutte le conseguenze

Altri test, anche se gli strumenti stanno migliorando sempre di più. MA dovresti definire il comportamento della classe in un'interfaccia (vedi sotto). NB ci sarà sempre un posto per i test manuali in cima alla piramide dei test.

Non dovremmo concentrarci maggiormente sui test di integrazione?

Sempre più test di integrazione non sono neanche la risposta, sono costosi da scrivere, eseguire e mantenere. A seconda della configurazione della build, il gestore della build può escluderli comunque facendoli ricorrere al ricordo di uno sviluppatore (mai una buona cosa!).

Ho visto gli sviluppatori passare ore a cercare di risolvere i test di integrazione rotti che avrebbero trovato in cinque minuti se avessero avuto buoni test unitari. In caso contrario, prova solo a eseguire il software: è tutto ciò che interesserà agli utenti finali. Non ha senso avere milioni di unit test che superano se l'intero castello di carte cade quando l'utente esegue l'intera suite.

Se vuoi assicurarti che la classe A consumi la classe X allo stesso modo, dovresti usare un'interfaccia piuttosto che una concrezione. Quindi è più probabile che un cambiamento di rottura venga rilevato al momento della compilazione.


9

È corretto.

I test unitari sono lì per testare la funzionalità isolata di un'unità, un controllo a prima vista che funziona come previsto e non contiene errori stupidi.

I test unitari non sono lì per verificare che l'intera applicazione funzioni.

Ciò che molte persone dimenticano è che i test unitari sono solo i mezzi più rapidi e più sporchi per convalidare il tuo codice. Una volta che sai che le tue piccole routine funzionano, devi anche eseguire i test di integrazione. Il test unitario da solo è solo leggermente migliore di nessun test.

Il motivo per cui abbiamo dei test unitari è che dovrebbero essere economici. Veloce da creare, eseguire e gestire. Una volta che inizi a trasformarli in test di integrazione min, sei in un mondo di dolore. Potresti anche completare il test di integrazione e ignorare del tutto il test unitario se lo farai.

ora, alcune persone pensano che un'unità non sia solo una funzione in una classe, ma l'intera classe stessa (me compreso). Tuttavia, tutto ciò che fa è aumentare le dimensioni dell'unità, quindi potresti aver bisogno di meno test di integrazione, ma ne hai ancora bisogno. È ancora impossibile verificare che il programma faccia quello che dovrebbe fare senza una suite di test di integrazione completa.

e quindi, dovrai comunque eseguire il test di integrazione completo su un sistema live (o semi-live) per verificare che funzioni con le condizioni utilizzate dal cliente.


2

I test unitari non dimostrano la correttezza di nulla. Questo è vero per tutti i test. Di solito i test unitari sono combinati con la progettazione basata su contratto (la progettazione per contratto è un altro modo di dirlo) e possibilmente prove automatizzate di correttezza, se la correttezza deve essere verificata su base regolare.

Se si dispone di contratti reali, costituiti da invarianti di classe, precondizioni e condizioni postali, è possibile dimostrare gerarchicamente la correttezza, basando la correttezza delle componenti di livello superiore sui contratti di componenti di livello inferiore. Questo è il concetto fondamentale alla base della progettazione per contratto.


I test unitari non dimostrano la correttezza di nulla . Non sono sicuro di averlo capito, i test unitari verificano sicuramente i propri risultati? O intendevi dire che un comportamento non può essere dimostrato corretto dal momento che può comprendere più livelli?
Robbie Dee,

7
@RobbieDee credo, intendeva dire che quando si prova per fac(5) == 120, si è non provato che fac()fa davvero tornare il fattoriale del suo argomento. Hai dimostrato che fac()restituisce il fattoriale di cinque quando passi 5. E anche questo non è certo, in quanto fac()potrebbe presumibilmente tornare 42invece il primo lunedì dopo un'eclissi totale a Timbuktu ... Il problema qui è che non è possibile dimostrare la conformità controllando i singoli input di test, è necessario controllare tutti gli input possibili, e dimostra anche che non ne hai dimenticato nessuno (come leggere l'orologio di sistema).
cmaster

1
I test @RobbieDee (compresi i test unitari) sono un sostituto scadente, spesso il migliore disponibile, per il vero obiettivo, una prova controllata dalla macchina. Considerare l'intero spazio degli stati delle unità in prova, compreso lo spazio degli stati di qualsiasi componente o derisione in essi. A meno che non si disponga di uno spazio degli stati molto limitato, i test non possono coprire tale spazio degli stati. La copertura completa sarebbe una prova, ma è disponibile solo per piccoli spazi di stato, ad esempio testando un singolo oggetto contenente un singolo byte mutabile o intero a 16 bit. Le prove automatizzate sono di gran lunga più preziose.
Frank Hileman,

1
@cmaster Hai riassunto molto bene la differenza tra un test e una prova. Grazie!
Frank Hileman,

2

Trovo che i test pesantemente derisi raramente siano utili. Il più delle volte, finisco per reimplementare il comportamento che la classe originale ha già, che sconfigge totalmente lo scopo del deridere.

Per me una strategia migliore è quella di avere una buona separazione delle preoccupazioni (ad esempio puoi testare la parte A della tua app senza includere le parti da B a Z). Un'architettura così buona aiuta davvero a scrivere buoni test.

Inoltre, sono più che disposto ad accettare gli effetti collaterali fintanto che posso ripristinarli, ad esempio se il mio metodo modifica i dati nel db, lascialo! Finché posso riportare il db allo stato precedente, che male c'è? Inoltre, c'è il vantaggio che il mio test può verificare se i dati sembrano come previsti. DB in memoria o specifiche versioni di test di dbs sono di grande aiuto qui (ad es. Versione di test in memoria di RavenDB).

Infine, mi piace prendere in giro i confini del servizio, ad esempio non effettuare quella chiamata http al servizio b, ma intercettiamolo e introduciamo un appropiato


1

Vorrei che le persone di entrambi i campi capissero che i test di classe e quelli di comportamento non sono ortogonali.

Test di classe e test unitari sono usati in modo intercambiabile e forse non dovrebbero esserlo. Alcuni test unitari vengono implementati in classi. Questo è tutto. I test unitari si svolgono da decenni in lingue senza lezioni.

Per quanto riguarda i comportamenti di test, è perfettamente possibile farlo all'interno dei test di classe usando il costrutto GWT.

Inoltre, il fatto che i test automatici procedano lungo le linee di classe o di comportamento dipende piuttosto dalle priorità. Alcuni dovranno prototipare rapidamente e ottenere qualcosa fuori dalla porta, mentre altri avranno vincoli di copertura dovuti a stili di casa. Molte ragioni. Sono entrambi approcci perfettamente validi. Paghi i tuoi soldi, fai la tua scelta.

Quindi, cosa fare quando il codice si rompe. Se è stato codificato su un'interfaccia, è necessario modificare solo la concrezione (insieme a tutti i test).

Tuttavia, l'introduzione di un nuovo comportamento non deve assolutamente compromettere il sistema. Linux e altri sono pieni di funzionalità deprecate. E cose come i costruttori (e i metodi) possono essere felicemente sovraccaricati senza forzare il cambiamento di tutto il codice chiamante.

Dove vince il test di classe è dove è necessario apportare una modifica a una classe che non è ancora stata inserita (a causa di vincoli di tempo, complessità o altro). È molto più semplice iniziare con una classe se ha test completi.


Dove hai concrezione avrei scritto l' implementazione . È un calco di un'altra lingua (immagino il francese) o c'è un'importante distinzione tra concrezione e implementazione ?
Peter Taylor,

0

A meno che l'interfaccia per X non sia cambiata, non è necessario modificare il test unitario per A perché non è cambiato nulla relativo a A. Sembra che tu abbia davvero scritto un unit test di X e A insieme, ma lo hai chiamato un unit test di A:

Quando scrivi test unitari per A, deridi X. In altre parole, mentre test unitario A, imposti (postulato) il comportamento di derisione di X su X1.

Idealmente, la simulazione di X dovrebbe simulare tutti i possibili comportamenti di X, non solo il comportamento che ci si aspetta da X. Quindi, indipendentemente da ciò che effettivamente si implementa in X, A dovrebbe già essere in grado di gestirlo. Quindi nessuna modifica a X, oltre a cambiare l'interfaccia stessa, avrà alcun effetto sul test unitario per A.

Ad esempio: Supponiamo che A sia un algoritmo di ordinamento e A fornisca i dati da ordinare. La simulazione di X dovrebbe fornire un valore di ritorno nullo, un elenco vuoto di dati, un singolo elemento, più elementi già ordinati, più elementi non già ordinati, più elementi ordinati all'indietro, elenchi con lo stesso elemento ripetuto, valori null mescolati, in modo ridicolo un gran numero di elementi, e dovrebbe anche generare un'eccezione.

Quindi forse X inizialmente ha restituito dati ordinati il ​​lunedì e liste vuote il martedì. Ma ora quando X restituisce dati non ordinati il ​​lunedì e genera eccezioni il martedì, A non se ne preoccupa: quegli scenari erano già coperti nel test unitario di A.


cosa succede se X ha appena rinominato l'indice nell'array restituito da foobar a foobarRandomNumber, come posso contare con quello? se capisci il punto, questo è fondamentalmente il mio problema, ho rinominato una colonna restituita da secondName a cognome, un compito classico, ma il mio test non lo saprà mai, dal momento che è stato deriso. Ho solo una strana sensazione come se molte persone in questa domanda non
avessero

Il compilatore avrebbe dovuto rilevare questa modifica e fornirti un errore del compilatore. Se stai usando qualcosa come Javascript, allora ti consiglio di passare a Typescript o di usare un compilatore come Babel in grado di rilevare queste cose.
Moby Disk,

Che cosa succede se sto usando array in PHP, o in Java o in Javascript, se cambi un indice di array o lo rimuovi, i compilatori non di quelle lingue ti diranno su di esso, l'indice potrebbe essere nidificato al 36 ° - pensato numero livello nidificato dell'array, quindi penso che il compilatore non sia la soluzione per questo.
FantomX1

-2

Devi guardare diversi test.

I test unitari eseguiranno solo il test di X. Sono lì per impedirti di modificare il comportamento di X ma non proteggere l'intero sistema. Assicurano che tu possa refactoring la tua classe senza introdurre un cambiamento nel comportamento. E se rompi X, l' hai rotto ...

A dovrebbe effettivamente deridere X per i suoi test unitari e il test con il Mock dovrebbe continuare a passare anche dopo averlo modificato.

Ma esiste più di un livello di test! Esistono anche test di integrazione. Questi test servono a convalidare l'interazione tra le classi. Questi test di solito hanno un prezzo più alto poiché non usano beffe per tutto. Ad esempio, un test di integrazione potrebbe effettivamente scrivere un record in un database e un test unitario non dovrebbe avere dipendenze esterne.

Inoltre, se X deve avere un nuovo comportamento, sarebbe meglio fornire un nuovo metodo in grado di fornire il risultato desiderato

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.