I test di integrazione (database) sono errati?


120

Alcune persone sostengono che i test di integrazione sono tutti i tipi di cattivi e sbagliati - tutto deve essere testato in unità, il che significa che devi deridere le dipendenze; un'opzione che, per vari motivi, non mi piace sempre.

Trovo che, in alcuni casi, un test unitario semplicemente non provi nulla.

Prendiamo come esempio la seguente (banale, ingenua) implementazione del repository (in PHP):

class ProductRepository
{
    private $db;

    public function __construct(ConnectionInterface $db) {
        $this->db = $db;
    }

    public function findByKeyword($keyword) {
        // this might have a query builder, keyword processing, etc. - this is
        // a totally naive example just to illustrate the DB dependency, mkay?

        return $this->db->fetch("SELECT * FROM products p"
            . " WHERE p.name LIKE :keyword", ['keyword' => $keyword]);
    }
}

Diciamo che voglio dimostrare in un test che questo repository può effettivamente trovare prodotti che corrispondono a varie parole chiave fornite.

A corto di test di integrazione con un oggetto di connessione reale, come posso sapere che questo sta effettivamente generando query reali e che quelle query fanno effettivamente quello che penso facciano?

Se devo deridere l'oggetto di connessione in un'unità-test, posso solo provare cose come "che genera la query previsto" - ma questo non significa che in realtà è di andare a lavorare ... cioè, forse è la generazione di query Mi aspettavo, ma forse quella query non fa quello che penso faccia.

In altre parole, mi sento come un test che fa affermazioni circa la query generato, è essenzialmente privo di valore, perché è come il test findByKeyword()è stato il metodo attuato , ma che non prova che in realtà funziona .

Questo problema non si limita ai repository o all'integrazione del database: sembra applicarsi in molti casi, in cui fare affermazioni sull'uso di una simulazione (test-double) dimostra solo come vengono implementate le cose, non se lo faranno funziona davvero.

Come gestisci situazioni come queste?

I test di integrazione sono davvero "cattivi" in un caso come questo?

Capisco che è meglio testare una cosa e capisco anche perché il test di integrazione porta a una miriade di percorsi di codice, tutti i quali non possono essere testati, ma nel caso di un servizio (come un repository) il cui unico scopo è per interagire con un altro componente, come puoi davvero testare qualsiasi cosa senza test di integrazione?


5
Leggi agitar.com/downloads/TheWayOfTestivus.pdf , in particolare pagina 6 "Il test è più importante dell'unità".
Doc Brown,

2
@ user61852 dice "ingenuo" nella descrizione, sì?
mindplay.dk,

4
In che modo il tuo collega sarebbe assolutamente certo che il suo database beffardo si comporta come la cosa reale?
Thorbjørn Ravn Andersen,

12
Stai cercando di essere realistico. Il tuo collega sta cercando di aderire alle regole. Scrivi sempre test che producono valore . Non perdere tempo a scrivere test che non saranno stampabili e non scrivere test che non eseguono una delle seguenti operazioni: aumenta la probabilità che il tuo codice sia corretto o ti costringe a scrivere codice più gestibile.
jpmc26,

3
@ mindplay.dk: la frase chiave in quel paragrafo è "Ma non rimanere bloccato su nessun dogma. Scrivi il test che deve essere scritto." Il tuo collega sembra essere bloccato in un dogma. E non hai bisogno di qualcuno spiegandoti qual è il test che deve essere scritto nel tuo esempio - lo sai già. È abbastanza ovvio che per testare se il tuo database comprende la query, devi eseguire la query sul database reale - non puoi dirti nulla questo
Doc Brown,

Risposte:


129

Il tuo collega ha ragione sul fatto che tutto ciò che può essere testato sull'unità dovrebbe essere testato sull'unità, e hai ragione sul fatto che i test unitari ti porteranno solo così lontano e non oltre, in particolare quando scrivi semplici wrapper attorno a servizi esterni complessi.

Un modo comune di pensare ai test è come una piramide di test . È un concetto spesso collegato ad Agile, e molti ne hanno scritto, tra cui Martin Fowler (che lo attribuisce a Mike Cohn in Successo con Agile ), Alistair Scott e il Blog Testing Blog .

        /\                           --------------
       /  \        UI / End-to-End    \          /
      /----\                           \--------/
     /      \     Integration/System    \      /
    /--------\                           \----/
   /          \          Unit             \  /
  --------------                           \/
  Pyramid (good)                   Ice cream cone (bad)

L'idea è che i test unitari resilienti e veloci siano alla base del processo di test: dovrebbero esserci test unitari più mirati rispetto ai test di sistema / integrazione e più test di sistema / integrazione rispetto ai test end-to-end. Man mano che ci si avvicina alla cima, i test tendono a richiedere più tempo / risorse per essere eseguiti, tendono ad essere soggetti a maggiore fragilità e sfaldamento e sono meno specifici nell'identificare quale sistema o file è rotto ; naturalmente, è preferibile evitare di essere "molto pesanti".

A quel punto, i test di integrazione non sono male , ma una forte dipendenza da essi può indicare che non hai progettato i tuoi singoli componenti per essere facili da testare. Ricorda, l'obiettivo qui è quello di testare che la tua unità sta eseguendo le sue specifiche mentre coinvolge un minimo di altri sistemi fragili : potresti voler provare un database in memoria (che io considero un test test unit-friendly doppio accanto a beffe ) per test su casi limite gravi, ad esempio, quindi scrivere un paio di test di integrazione con il motore di database reale per stabilire che i casi principali funzionano quando il sistema è assemblato.


Come nota a margine, hai detto che le beffe che scrivi semplicemente testano come viene implementato qualcosa, non se funziona . È una specie di antipasto: un test che è uno specchio perfetto della sua implementazione non sta davvero testando nulla. Invece, verifica che ogni classe o metodo si comporti secondo le proprie specifiche , a qualunque livello di astrazione o realismo che richieda.


13
+1 per "Un test che è il perfetto specchio della sua implementazione non sta davvero testando nulla". Troppo comune. Questo lo chiamo Dopelganger Antipattern .
dodgethesteamroller,

6
Una scuola contrarian di software QA, il movimento di test guidato dal contesto , è in parte dedicata a contestare l'esistenza di eventuali regole generali utili come la Piramide dei test. In particolare, i testi fondamentali del movimento forniscono molti esempi in cui i test di integrazione sono molto più preziosi di altri tipi di test (perché testano il sistema nel contesto, come sistema ) ....
dodgethesteamroller,

7
... di conseguenza, Fowler et al., sostenendo che si dovrebbero dedicare meno sforzi ai test di integrazione e ai test di accettazione dell'utente perché sono troppo difficili da scrivere in modo robusto e mantenibile, stanno davvero fornendo una scusa ex post per il perché non hanno capito come testare bene ai livelli più alti.
dodgethesteamroller,

1
@dodgethesteamroller Discussioni approfondite di una "scuola contrarian" come quella sono probabilmente più adatte alla propria risposta. Personalmente, trovo che il Blog di test di Google faccia un buon lavoro descrivendo le virtù di test automatici rapidi e strettamente mirati insieme a test di sistema nel contesto. Nel caso in cui la trovassi poco chiara, elencherò qui la piramide di prova come modello utile o punto di partenza, non come una scusa per smettere di pensare come ingegnere.
Jeff Bowman,

1
Presentazione altamente raccomandata della gerarchia e del rapporto tra unittest e test di integrazione: vimeo.com/80533536 Molto ben spiegato.
szalski,

88

Uno dei miei colleghi sostiene che i test di integrazione sono tutti i tipi di cattivi e sbagliati: tutto deve essere testato,

È un po 'come dire che gli antibiotici sono cattivi: tutto dovrebbe essere curato con le vitamine.

I test unitari non riescono a catturare tutto: testano solo il funzionamento di un componente in un ambiente controllato . I test di integrazione verificano che tutto funzioni insieme , il che è più difficile da fare ma alla fine più significativo.

Un processo di test completo e valido utilizza entrambi i tipi di test: test unitari per verificare le regole aziendali e altre cose che possono essere testate in modo indipendente e test di integrazione per assicurarsi che tutto funzioni insieme.

A corto di test di integrazione con un oggetto di connessione reale, come posso sapere che questo sta effettivamente generando query reali e che quelle query fanno effettivamente quello che penso facciano?

È possibile testarlo a livello di database . Eseguire la query con vari parametri e vedere se si ottengono i risultati previsti. Concesso, significa copiare / incollare tutte le modifiche nel codice "vero". ma non consentono di testare indipendente interrogazione di eventuali altre dipendenze.


Non testereste se il database contiene o meno determinati dati?
Tulains Córdova,

Forse - ma potresti anche provare che i tuoi filtri, join complessi, ecc. Funzionino. La query di esempio probabilmente non è la migliore candidata per "unit test", ma potrebbe esserci una con join e / o aggregazioni complesse.
D Stanley,

Sì, l'esempio che ho usato, come sottolineato, è banale; un vero repository potrebbe avere ogni sorta di complesse opzioni di ricerca e ordinamento, ad esempio utilizzando un generatore di query, ecc.
mindplay.dk

2
Buona risposta, ma aggiungerei che il DB dovrebbe essere in memoria, per assicurarsi che i test unitari siano veloci.
BЈовић,

3
@BЈовић: Sfortunatamente, potrebbe non essere sempre possibile, dal momento che sfortunatamente non ci sono due DB compatibili e non tutti funzionano in memoria. Ci sono anche problemi di licenza per DB commerciali (potresti non avere la licenza per eseguirla su qualsiasi macchina), ...
Matthieu M.

17

I test unitari non rilevano tutti i difetti. Ma sono più economici da installare e (ri) eseguire rispetto ad altri tipi di test. I test unitari sono giustificati dalla combinazione di valore moderato e costo da basso a moderato.

Ecco una tabella che mostra i tassi di rilevamento dei difetti per diversi tipi di test.

inserisci qui la descrizione dell'immagine

fonte: p.470 in Code Complete 2 di McConnell


5
Tali dati sono stati raccolti nel 1986 . Questo è stato trenta anni fa . I test unitari nel 1986 non erano più quelli di oggi. Sarei scettico su questi dati. Per non parlare, i test unitari rilevano il bug prima che sia mai stato commesso , quindi è dubbio che vengano segnalati.
RubberDuck,

3
@RubberDuck Questo grafico è tratto da un libro del 2006 ed è basato su dati del 1986, 1996, 2002 (se osservi attentamente). Non ho esaminato i protocolli di raccolta dei dati nelle fonti, non posso dire quando hanno iniziato a rilevare un difetto e come è stato segnalato. Questo grafico potrebbe essere in qualche modo obsoleto? Potrebbe. Lo scorso dicembre ero a un seminario e l'istruttore ha menzionato che i test di integrazione trovano più bug che test unitari (con un fattore 2, iirc). Questo grafico dice che sono più o meno gli stessi.
Nick Alexeev,

13

No, non sono male. Si spera che si dovrebbero avere test unitari e di integrazione. Vengono utilizzati e eseguiti in diverse fasi del ciclo di sviluppo.

Test unitari

I test unitari devono essere eseguiti sul server di compilazione e localmente, dopo che il codice è stato compilato. Se qualche test unitario fallisce, si dovrebbe fallire la compilazione o non eseguire il commit dell'aggiornamento del codice fino a quando i test non vengono risolti. Il motivo per cui vogliamo che i test unitari siano isolati è che vogliamo che il server di build sia in grado di eseguire tutti i test senza tutte le dipendenze. Quindi potremmo eseguire la build senza tutte le complesse dipendenze richieste e avere molti test eseguiti molto velocemente.

Quindi, per un database, si dovrebbe avere qualcosa del tipo:

IRespository

List<Product> GetProducts<String Size, String Color);

Ora la vera implementazione di IRepository andrà al database per ottenere i prodotti, ma per i test unitari, si può prendere in giro IRepository con uno falso per eseguire tutti i test necessari senza un database actaul in quanto possiamo simulare tutti i tipi di elenchi di prodotti essere restituito dall'istanza finta e testare qualsiasi logica aziendale con i dati derisi.

Test di integrazione

I test di integrazione sono in genere test di attraversamento dei confini. Vogliamo eseguire questi test sul server di distribuzione (l'ambiente reale), sandbox o anche localmente (indicato su sandbox). Non vengono eseguiti sul server di compilazione. Dopo che il software è stato distribuito nell'ambiente, in genere questi vengono eseguiti come attività post-distribuzione. Possono essere automatizzati tramite le utilità della riga di comando. Ad esempio, possiamo eseguire nUnit dalla riga di comando se classifichiamo tutti i test di integrazione che vogliamo invocare. Questi in realtà chiamano il vero repository con la vera chiamata al database. Questo tipo di test aiuta con:

  • Ambiente Disponibilità alla stabilità
  • Testare la cosa reale

A volte questi test sono più difficili da eseguire poiché potrebbe essere necessario impostare e / o anche demolire. Valuta di aggiungere un prodotto. Probabilmente vogliamo aggiungere il prodotto, interrogarlo per vedere se è stato aggiunto e, dopo aver finito, rimuoverlo. Non vogliamo aggiungere 100 o 1000 prodotti di "integrazione", quindi è necessario un set-up aggiuntivo.

I test di integrazione possono rivelarsi molto utili per convalidare un ambiente e assicurarsi che la cosa reale funzioni.

Uno dovrebbe avere entrambi.

  • Esegui i test unitari per ogni build.
  • Eseguire i test di integrazione per ogni distribuzione.

Consiglierei di eseguire test di integrazione per ogni build, piuttosto che dover impegnare e spingere. Dipende da quanto tempo impiegano, ma è anche una buona idea mantenerli veloci per molte ragioni.
artbristol

@ArtBristol - In genere il nostro server di build non ha la dipendenza completa dall'ambiente configurata, quindi non possiamo eseguire i nostri test di integrazione lì. Ma se uno può eseguire i test di integrazione lì, provaci. Dopo la compilazione abbiamo installato un sandbox di distribuzione che utilizziamo per i test di integrazione per verificare la distribuzione. Ma ogni situazione è diversa.
Jon Raynor,

11

I test di integrazione del database non sono male. Ancora di più, sono necessari.

Probabilmente l'applicazione è suddivisa in livelli ed è una buona cosa. Puoi testare ogni livello in isolamento prendendo in giro i livelli vicini, e anche questa è una buona cosa. Ma non importa quanti livelli di astrazione crei, ad un certo punto deve esserci un livello che fa il lavoro sporco - in realtà parla con il database. A meno che non lo provi, non lo fai affatto. Se testate il livello n deridendo il livello n-1 state valutando l'assunto che il livello n funzioni a condizione che il livello n-1 funzioni. Affinché ciò funzioni, devi in ​​qualche modo provare che il layer 0 funziona.

Mentre in teoria è possibile unità di database di test, analizzando e interpretando l'SQL generato, è molto più semplice e affidabile creare un database di test al volo e parlarne.

Conclusione

Qual è la fiducia derivante dall'unità che verifica i repository astratti , il mappatore di oggetti eterici relazionali , i record attivi generici , i livelli di persistenza teorica , quando alla fine l'SQL generato contiene errori di sintassi?


Stavo pensando di aggiungere una risposta simile alla tua ma l'hai detto meglio! Nella mia esperienza, avere alcuni test sul livello che ottiene e memorizza i dati mi ha risparmiato molto dolore.
Daniel Hollinrake,

I test di integrazione del database non sono buoni. Hai un database disponibile nella tua linea ci / cd? Mi sembra piuttosto complesso. È molto più facile prendere in giro le cose del database e costruire un livello di astrazione per usarlo. Non solo è una soluzione molto più elegante, ma è anche la più veloce possibile. I test unitari devono essere veloci. Il test del database rallenta notevolmente gli unittest a un livello inaccettabile. Gli unittest non dovrebbero richiedere> 10 minuti, anche se ne hai migliaia.
David,

@David Hai un database disponibile nella tua linea ci / cd? Certo, questa è una funzionalità piuttosto standard . A proposito, non sto sostenendo i test di integrazione invece dei test unitari - sto sostenendo i test di integrazione insieme ai test unitari. I test unitari rapidi sono essenziali, ma i database sono troppo complessi per fare affidamento sui test unitari con interazioni derise.
el.pescado,

@ el.pescado Non sono d'accordo. Se la comunicazione del database è dietro uno strato di astrazione, è davvero facile da deridere. Puoi semplicemente decidere quale oggetto restituire. Inoltre, il fatto che qualcosa sia standard non lo rende una buona cosa.
David,

@ David Penso che dipenda da come ti avvicini al database. Sono i dettagli di implementazione o la parte vitale del sistema ? (Mi sposto verso quest'ultimo) . Se trattate il database come una stupida memoria di dati, allora sì, potreste fare a meno dei test di integrazione. Tuttavia, se nel database è presente una logica: vincoli, trigger, chiavi esterne, transazioni o il livello dati utilizza SQL personalizzato anziché semplici metodi ORM, ritengo che i test unitari da soli non saranno sufficienti.
el.pescado,

6

Hai bisogno di entrambi.

Nel tuo esempio, se stavi testando un database in una determinata condizione, quando il findByKeywordmetodo viene eseguito, recuperi i dati e ti aspetti che si tratti di un ottimo test di integrazione.

In qualsiasi altro codice che utilizza quel findByKeywordmetodo si desidera controllare ciò che viene inserito nel test, in modo da poter restituire valori nulli o le parole giuste per il test o qualsiasi altra cosa, quindi si deride la dipendenza del database in modo da sapere esattamente quale sarà il test ricevi (e perdi il sovraccarico di collegarti a un database e assicurarti che i dati all'interno siano corretti)


6

L'autore dell'articolo del blog a cui ti riferisci si preoccupa principalmente della potenziale complessità che può derivare da test integrati (sebbene sia scritto in modo molto ponderato e categorico). Tuttavia, i test integrati non sono necessariamente negativi e alcuni sono in realtà più utili dei test unitari puri. Dipende molto dal contesto dell'applicazione e da cosa stai provando a testare.

Molte applicazioni oggi semplicemente non funzionerebbero affatto se il loro server di database non funzionasse. Almeno, pensaci nel contesto della funzione che stai provando a testare.

Da un lato, se ciò che si sta tentando di verificare non dipende o si può fare in modo che non dipenda affatto dal database, scrivere il test in modo tale da non provare nemmeno a utilizzare il database (basta fornire dati fittizi come richiesto). Ad esempio, se stai provando a testare alcune logiche di autenticazione quando servi una pagina web (per esempio), è probabilmente una buona cosa staccarlo del tutto dal DB (supponendo che tu non faccia affidamento sul DB per l'autenticazione, o che puoi deriderlo ragionevolmente facilmente).

D'altra parte, se si tratta di una funzionalità che si basa direttamente sul tuo database e che non funzionerebbe affatto in un ambiente reale se il database non fosse disponibile, beffardo ciò che fa il DB nel tuo codice client DB (ovvero il layer che lo utilizza DB) non ha necessariamente senso.

Ad esempio, se sai che la tua applicazione farà affidamento su un database (e possibilmente su un sistema di database specifico), deridere il comportamento del database per il gusto di farlo spesso sarà una perdita di tempo. I motori di database (in particolare RDBMS) sono sistemi complessi. Alcune righe di SQL possono effettivamente eseguire molto lavoro, il che sarebbe difficile da simulare (in effetti, se la tua query SQL è lunga poche righe, è probabile che avrai bisogno di molte più righe di Java / PHP / C # / Python codice per produrre internamente lo stesso risultato): duplicare la logica che hai già implementato nel DB non ha senso e verificare che il codice di test diventi un problema in sé.

Non lo tratterei necessariamente come un problema di unit test vs test integrato , ma guarderei piuttosto alla portata di ciò che viene testato. Rimangono i problemi generali dei test unitari e di integrazione: è necessario un insieme ragionevolmente realistico di dati di test e casi di test, ma qualcosa di sufficientemente piccolo da consentire l'esecuzione rapida dei test.

Il tempo di ripristinare il database e ripopolare con i dati di test è un aspetto da considerare; generalmente lo valuteresti rispetto al tempo impiegato per scrivere quel codice simulato (che dovresti anche mantenere, eventualmente).

Un altro punto da considerare è il grado di dipendenza che l'applicazione ha con il database.

  • Se la tua applicazione segue semplicemente un modello CRUD, in cui hai uno strato di astrazione che ti consente di scambiare tra qualsiasi RDBMS con il semplice mezzo di un'impostazione di configurazione, è probabile che sarai in grado di lavorare con un sistema simulato abbastanza facilmente (eventualmente sfocando la linea tra unità e test integrato utilizzando un RDBMS in memoria).
  • Se l'applicazione utilizza una logica più complessa, qualcosa che sarebbe specifico per uno di SQL Server, MySQL, PostgreSQL (ad esempio), allora sarebbe più sensato avere un test che utilizza quel sistema specifico.

"Molte applicazioni oggi semplicemente non funzionerebbero affatto se il loro server di database non funzionasse" - questo è un punto davvero importante!
el.pescado,

Buona spiegazione dei limiti di simulazioni complesse, come usare un altro linguaggio per deridere SQL. Quando il codice di test diventa abbastanza complicato da sembrare che debba essere testato da solo, questo è un odore di QA.
dodgethesteamroller,

1

Hai ragione a pensare che un tale test unitario sia incompleto. L'incompletezza è stata derisa nell'interfaccia del database. Le aspettative o le asserzioni di tale finta ingenua sono incomplete.

Per completarlo, dovresti risparmiare abbastanza tempo e risorse per scrivere o integrare un motore di regole SQL che garantisca che l'istruzione SQL emessa dall'oggetto in prova comporti operazioni previste.

Tuttavia, l'alternativa / compagna del deridere spesso dimenticata e alquanto costosa è la "virtualizzazione" .

È possibile creare un'istanza DB temporanea, in memoria ma "reale" per testare una singola funzione? sì ? lì, hai un test migliore, quello che controlla i dati effettivi salvati e recuperati.

Ora, si potrebbe dire, hai trasformato un test unitario in un test di integrazione. Esistono viste diverse su dove tracciare la linea per classificare tra test unitari e test di integrazione. IMHO, "unità" è una definizione arbitraria e dovrebbe adattarsi alle tue esigenze.


1
questo sembra semplicemente ripetere i punti fatti e spiegati in questa precedente risposta che è stata pubblicata diverse ore fa
moscerino

0

Unit Testse Integration Testssono ortogonali tra loro. Offrono una visione diversa dell'applicazione che stai creando. Di solito vuoi entrambi . Ma il punto nel tempo differisce, quando vuoi che tipo di test.

Più spesso vuoi Unit Tests. I test unitari si concentrano su una piccola parte del codice che viene testato: ciò che viene chiamato esattamente a unitviene lasciato al lettore. Ma lo scopo è semplice: ottenere un feedback rapido su quando e dove si è rotto il codice . Detto questo, dovrebbe essere chiaro, che le chiamate a un DB reale non sono valide .

D'altra parte, ci sono cose che possono essere testate solo in condizioni difficili senza un database. Forse c'è una condizione di competizione nel tuo codice e una chiamata a un DB genera una violazione di un unique constraintche potrebbe essere lanciata solo se utilizzi effettivamente il tuo sistema. Ma questi tipi di test sono costosi e non è possibile (e non si desidera) eseguirli tutte le volte che unit tests.


0

Nel mondo .Net ho l'abitudine di creare un progetto di test e creare test come metodo di codifica / debug / test di andata e ritorno meno l'interfaccia utente. Questo è un modo efficace per svilupparmi. Non ero interessato a eseguire tutti i test per ogni build (perché rallenta il mio flusso di lavoro di sviluppo), ma capisco l'utilità di questo per un team più ampio. Tuttavia, è possibile stabilire una regola che prima di eseguire il commit del codice, tutti i test devono essere eseguiti e passati (se impiega più tempo a eseguire i test perché il database viene effettivamente colpito).

Deridere il livello di accesso ai dati (DAO) e non colpire effettivamente il database, non solo non mi permette di programmare il modo in cui mi piace e mi sono abituato, ma manca una grande parte della base di codice reale. Se non stai veramente testando il livello di accesso ai dati e il database e stai solo fingendo, e poi stai spendendo molto tempo a prendere in giro le cose, non riesco a cogliere l'utilità di questo approccio per testare effettivamente il mio codice. Sto testando un piccolo pezzo invece di uno più grande con un test. Capisco che il mio approccio potrebbe essere più sulla falsariga di un test di integrazione, ma sembra che il test unitario con la simulazione sia una perdita di tempo ridondante se in realtà si scrive il test di integrazione una volta per prima. È anche un buon modo per sviluppare ed eseguire il debug.

In effetti, da un po 'di tempo sono a conoscenza di TDD e Behavior Driven Design (BDD) e sto pensando a come usarlo, ma è difficile aggiungere test unitari retroattivamente. Forse mi sbaglio, ma scrivere un test che copre più codice end-to-end con il database incluso, sembra un test molto più completo e con priorità più alta da scrivere che copre più codice ed è un modo più efficiente di scrivere test.

In effetti, penso che qualcosa come Behavior Driven Design (BDD) che tenta di testare end-to-end con un linguaggio specifico di dominio (DSL) dovrebbe essere la strada da percorrere. Abbiamo SpecFlow nel mondo .Net, ma è iniziato come open source con Cucumber.

https://cucumber.io/

Non sono davvero impressionato dalla vera utilità del test che ho scritto deridendo il livello di accesso ai dati e non colpendo il database. L'oggetto restituito non ha colpito il database e non è stato popolato con dati. Era un oggetto completamente vuoto che ho dovuto deridere in modo innaturale. Penso solo che sia una perdita di tempo.

Secondo Stack Overflow, il derisione viene utilizzato quando oggetti reali non sono pratici da incorporare nel test unitario.

https://stackoverflow.com/questions/2665812/what-is-mocking

"Il derisione viene utilizzato principalmente nel test unitario. Un oggetto in prova può avere dipendenze da altri oggetti (complessi). Per isolare il comportamento dell'oggetto che si desidera testare, sostituire gli altri oggetti con simulazioni che simulano il comportamento degli oggetti reali. Ciò è utile se gli oggetti reali non sono pratici da incorporare nel test unitario. "

La mia tesi è che se sto programmando qualcosa end-to-end (interfaccia utente web a livello aziendale a livello di accesso ai dati al database, round trip), prima di controllare qualcosa come sviluppatore, testerò questo flusso di round trip. Se interrompo l'interfaccia utente, eseguo il debug e collaudo questo flusso partendo da un test, sto testando tutto ciò che è al di fuori dell'interfaccia utente e restituendo esattamente ciò che l'interfaccia utente si aspetta. Non mi resta che inviare l'interfaccia utente come desidera.

Ho un test più completo che fa parte del mio flusso di lavoro di sviluppo naturale. Per me, questo dovrebbe essere il test con la massima priorità che copre il test delle specifiche dell'utente finale end to end il più possibile. Se non creo mai altri test più granulari, almeno ho questo test più completo che dimostra che la mia funzionalità desiderata funziona.

Un co-fondatore di Stack Exchange non è convinto dei vantaggi di avere una copertura unitaria del 100%. Anche io no. Vorrei fare un "test di integrazione" più completo che colpisca il database oltre a mantenere un sacco di beffe di database ogni giorno.

https://www.joelonsoftware.com/2009/01/31/from-podcast-38/


è così ovvio che non capisci la differenza tra unità e test di integrazione
BЈовић

Penso che dipenda dal progetto. Sui progetti più piccoli con meno risorse in cui uno sviluppatore è maggiormente responsabile dei test e dei test di regressione a causa della mancanza di tester e di mantenere la documentazione sincronizzata con il codice, se ho intenzione di passare del tempo a scrivere test, saranno quelli che mi dà il massimo per il mio dollaro. Voglio uccidere quanti più uccelli possibile con una fava. Se la maggior parte della mia logica e dei miei bug provengono da procedure memorizzate nel database che generano report o dal JavaScript front-end, avere una copertura completa dei test delle unità nel livello intermedio non aiuta molto.
user3198764

-1

Le dipendenze esterne dovrebbero essere derise perché non è possibile controllarle (potrebbero passare durante la fase di test di integrazione ma non riuscire nella produzione). Le unità possono fallire, le connessioni al database potrebbero non riuscire per una serie di motivi, potrebbero esserci problemi di rete, ecc. Avere test di integrazione non dà ulteriore sicurezza perché sono tutti problemi che potrebbero verificarsi in fase di esecuzione.

Con veri test unitari, stai testando entro i limiti della sandbox e dovrebbe essere chiaro. Se uno sviluppatore ha scritto una query SQL non riuscita in QA / PROD significa che non ha nemmeno testato una volta prima di quel momento.


+1 per "non puoi controllarli (potrebbero passare durante la fase di test di integrazione ma fallire nella produzione)" .
Tulains Córdova,

È possibile controllarli a satisfactionary grado.
el.pescado,

Capisco, ma penso che questo fosse più vero di quanto lo sia oggi? Con l'automazione e gli strumenti (come Docker) è possibile replicare e ripetere l'installazione di tutte le dipendenze binarie / server in modo accurato e affidabile per le suite di test di integrazione. Certo, sì, l'hardware fisico (e i servizi di terze parti ecc.) Possono fallire.
mindplay.dk,

5
Non sono assolutamente d'accordo. È necessario scrivere test di integrazione (aggiuntivi) perché le dipendenze esterne possono non riuscire. Le dipendenze esterne possono avere le loro stranezze, che molto probabilmente ti mancheranno quando deridi tutto.
Paul Kertscher,

1
@PaulK sta ancora riflettendo su quale risposta contrassegnare come accettata, ma mi sto inclinando verso la stessa conclusione.
mindplay.dk,
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.