I test per lo sviluppo guidato dai test (TDD) sono sempre test unitari?


41

Comprendo lo sviluppo guidato dai test finora che ti è permesso scrivere codice produttivo solo quando hai un test unit (rosso) fallito. Sulla base di questo ho la domanda se l'approccio test-driven può essere applicato anche ad altre forme di test.


6
Non è raro usare più di un diverso livello di rosso / verde / refactor annidati l'uno nell'altro. Ad esempio, è possibile seguire rosso / verde / refactor durante la scrittura di test di accettazione / comportamento, in cui la fase "verde" del test di accettazione stesso contiene più iterazioni rosse / verdi / refactor dei test unitari.
Sean Burton,

1
Il titolo non corrisponde al contenuto della domanda. Titolo "i test sono sempre test unitari " (risposta: no, possono esserci altri tipi di test oltre ai test unitari), il contenuto chiede "devi prima scrivere il test?".
AnoE

@AnoE La prima frase del contenuto è solo una frase introduttiva. La frase dei secondi non chiede se il test deve essere scritto per primo, ma se l'approccio TDD può essere utilizzato per metodi di test diversi da TDD.
user1364368

@ user1364368, sentiti libero di riformulare un po 'la domanda, almeno ero confuso su quale sia la tua intenzione in prima lettura, e la domanda più votata, mentre affrontare entrambe le tue frasi inizia in modo evidente con la prima.
AnoE

@AnoE Ho modificato l'inizio della seconda frase per chiarire qual è la vera domanda.
user1364368

Risposte:


27

Tutto ciò che TDD richiede da te è scrivere un test fallito, quindi modificare il codice per farlo passare.

In genere i "test unitari" sono piccoli e veloci e verificano una parte del codice in modo isolato. Perché sono veloci, rende veloce anche il ciclo rosso / verde / refactor. Tuttavia, soffrono solo del test di parti isolate. Quindi hai bisogno anche di altri test (integrazione, accettazione ecc.). È sempre buona norma seguire gli stessi principi: scrivere un test non riuscito, quindi modificare il codice per farlo funzionare. Basta essere consapevoli del fatto che sono generalmente più lenti, quindi possono influire sul tempo di ciclo rosso / verde / refactor.


59

Il ciclo del refattore verde rosso si basa su un principio molto valido:

Fidati solo dei test che hai visto passare e fallire.

Sì, funziona anche con i test di integrazione automatizzata. Anche test manuali. Diamine, funziona su tester per batterie per auto. Ecco come testare il test.

Alcuni pensano che i test unitari coprano la cosa più piccola che può essere testata. Alcuni pensano a tutto ciò che è veloce da testare. TDD è molto più che un semplice ciclo di rifattore verde rosso, ma quella parte ha una serie molto specifica di test: non sono i test che idealmente si eseguiranno una volta prima di presentare una raccolta di modifiche. Sono i test che eseguirai ogni volta che apporti delle modifiche. Per me, quelli sono i tuoi test unitari.


1
Questo è anche uno dei motivi per cui il test negativo è importante quando si verifica che si verifichi un errore: si desidera assicurarsi che tutto il resto avrebbe funzionato, quindi avere due test (uno che produce l'errore esatto previsto, l'altro che non produce l'errore) accanto a a vicenda aiuta ad aumentare la fiducia che in futuro questo stato rosso / verde continuerà.
Matthieu M.

12

Tuttavia, mi chiedo se l'approccio test-driven può essere applicato anche ad altre forme di test.

Sì, e un approccio ben noto che fa questo è lo sviluppo guidato dal comportamento . I test generati dalle specifiche formali in BDD potrebbero essere chiamati "test unitari", ma in genere non saranno così di basso livello come nel TDD reale, probabilmente si adatteranno meglio al termine "test di accettazione".


8

Comprendo lo sviluppo guidato dai test finora che ti è permesso scrivere codice produttivo solo quando hai un test unit (rosso) fallito.

No. Puoi solo scrivere il codice più semplice possibile per modificare il messaggio del test. Non dice nulla su che tipo di test.

In effetti, probabilmente inizierai scrivendo un test di accettazione (rosso) fallito per un criterio di accettazione, più precisamente, scrivi il test di accettazione più semplice che potrebbe eventualmente fallire; successivamente esegui il test, guardalo fallire e verifica che fallisca per la giusta ragione. Quindi scrivi un test funzionale non riuscito per una porzione di funzionalità di quel criterio di accettazione, ancora una volta, scrivi il test funzionale più semplice che potrebbe eventualmente fallire, eseguirlo, guardarlo fallire e verificare che fallisca per la giusta ragione. Quindi scrivi un test unitario fallito, il test unitario più semplice che potrebbe eventualmente fallire, eseguilo guardalo fallire, verifica che fallisca per la giusta ragione.

Ora scrivi il codice di produzione più semplice che potrebbe cambiare il messaggio di errore. Eseguire nuovamente il test, verificare che il messaggio di errore sia cambiato, che sia cambiato nella giusta direzione e che il codice abbia modificato il messaggio per il motivo corretto. (Idealmente, il messaggio di errore dovrebbe essere ormai scomparso e il test dovrebbe passare, ma il più delle volte, è meglio fare piccoli passi cambiando il messaggio invece di provare a far passare il test in una volta sola - questa è la ragione perché gli sviluppatori di framework di test dedicano così tanto impegno ai loro messaggi di errore!)

Una volta superato il test unitario, refactoring il codice di produzione sotto la protezione dei test. (Si noti che in questo momento, il test di accettazione e il test funzionale stanno ancora fallendo, ma va bene, dal momento che si stanno solo refactoring singole unità che sono coperte da test unitari.)

Ora crei il test unitario successivo e ripeti quanto sopra, fino a quando non passa anche il test funzionale. Sotto la protezione del test funzionale, ora è possibile eseguire refactoring su più unità.

Questo ciclo intermedio ora si ripete fino al superamento del test di collaudo, a quel punto è ora possibile eseguire i refactoring in tutto il sistema.

Ora scegli il criterio di accettazione successivo e il ciclo esterno ricomincia.

Kent Beck, lo "scopritore" di TDD (non gli piace il termine "inventore", dice che la gente lo ha sempre fatto, gli ha appena dato un nome e ne ha scritto un libro) usa un'analogia dalla fotografia e chiama questo "ingrandimento e riduzione".

Nota: non sono sempre necessari tre livelli di test. Forse, a volte hai bisogno di più. Più spesso, hai bisogno di meno. Se le tue funzionalità sono piccole e i test funzionali sono rapidi, puoi cavartela senza (o con meno test unitari). Spesso sono necessari solo test di collaudo e test unitari. Oppure, i criteri di accettazione sono così dettagliati che i test di accettazione sono test funzionali.

Kent Beck afferma che se ha un test funzionale veloce, piccolo e mirato, scriverà prima i test unitari, lascerà che i test unitari guidino il codice, quindi cancellerà (alcuni) dei test unitari che coprono anche il codice che è anche coperto dal test funzionale veloce. Ricorda: il codice di prova è anche codice che deve essere mantenuto e refactored, meno c'è, meglio è!

Tuttavia, mi chiedo se l'approccio test-driven può essere applicato anche ad altre forme di test.

Non si applica realmente il TDD ai test. Lo applichi all'intero processo di sviluppo. Questo è ciò che significa la parte "guidata" di Test- Driven- Development: tutto il tuo sviluppo è guidato da test. I test non guidano solo il codice che scrivi, ma guidano anche quale codice scrivere, quale codice scrivere successivamente. Guidano il tuo design. Ti dicono quando hai finito. Ti dicono su cosa lavorare dopo. Ti dicono dei difetti di progettazione nel tuo codice (quando i test sono difficili da scrivere).

Keith Braithwaite ha creato un esercizio che chiama TDD come se lo intendessi . Consiste in un insieme di regole (basate sulle Tre regole del TDD di zio Bob Martin , ma molto più rigorose) che devi seguire rigorosamente e che sono progettate per orientarti verso l'applicazione del TDD in modo più rigoroso. Funziona meglio con la programmazione della coppia (in modo che la tua coppia possa assicurarsi di non infrangere le regole) e un istruttore.

Le regole sono:

  1. Scrivi esattamente un nuovo test, il test più piccolo possibile che sembra puntare nella direzione di una soluzione
  2. Vederlo fallire; gli errori di compilazione vengono considerati errori
  3. Fai passare il test da (1) scrivendo il codice di implementazione minimo che puoi nel metodo di test .
  4. Rifattore per rimuovere la duplicazione, e altrimenti come richiesto per migliorare il design. Sii severo nell'usare queste mosse:
    1. vuoi un nuovo metodo: attendi il tempo di refactoring, quindi ... crea nuovi metodi (non di prova) eseguendo uno di questi e in nessun altro modo:
      • preferito: eseguire il metodo di estrazione sul codice di implementazione creato come da (3) per creare un nuovo metodo nella classe di test, oppure
      • se è necessario: spostare il codice di implementazione come da (3) in un metodo di implementazione esistente
    2. vuoi una nuova classe: attendi il tempo di refactoring, quindi ... crea classi non di prova per fornire una destinazione per un metodo di spostamento e per nessun altro motivo
    3. popolare le classi di implementazione con metodi facendo il metodo Move e nessun altro modo

Queste regole sono pensate per l'esercizio del TDD. Non sono pensati per fare effettivamente TDD in produzione (anche se nulla ti impedisce di provarlo). Possono sentirsi frustrati perché a volte sembrerà di fare migliaia di piccoli piccoli passi da teenager senza fare alcun progresso reale.


2

TDD non si limita affatto a ciò che la tradizionale comunità di Software Testing chiama "unit test". Questo fraintendimento molto comune è il risultato dello sfortunato sovraccarico del termine "unità" di Kent Beck nel descrivere la sua pratica di TDD. Ciò che intendeva per "unit test" era un test eseguito in modo isolato. Non dipende da altri test. Ogni test deve impostare lo stato di cui ha bisogno e fare qualsiasi pulizia al termine. È in questo senso che un test unitario nel senso TDD è un'unità. È autonomo. Può essere eseguito da solo o può essere eseguito insieme a qualsiasi altra unità di test in qualsiasi ordine.

Riferimento : "Test Driven Development by Example", di Kent Beck

Kent Beck descrive cosa intende per "unit test" nel Capitolo 32 - Mastering TDD


1

Non ho letto libri su di esso, né seguo sempre le pratiche "standard" di TDD, ma nella mia mente il punto principale della filosofia TDD, che sono completamente d'accordo, è che devi prima definire il successo . Questo è importante a tutti i livelli del design, da "Qual è l'obiettivo di questo progetto?" a "Quali dovrebbero essere gli input e gli output di questo piccolo metodo?"

Ci sono molti modi per fare questa definizione di successo. Un utile, in particolare per quei metodi di basso livello con potenzialmente molti casi limite, è scrivere test in codice. Per alcuni livelli di astrazione, può essere utile solo scrivere una breve nota sull'obiettivo del modulo o qualsiasi altra cosa, o anche solo controllare mentalmente te stesso (o chiedere a un collega) per assicurarsi che tutto abbia senso e sia in un luogo logico. A volte è utile descrivere un test di integrazione nel codice (e ovviamente ciò aiuta a automatizzarlo), a volte è utile solo definire un piano di test rapido ragionevole che è possibile utilizzare per assicurarsi che tutti i sistemi funzionino insieme nel modo in cui stanno aspettando.

Ma indipendentemente dalle tecniche o dagli strumenti specifici che stai usando, nella mia mente la cosa chiave da togliere alla filosofia TDD è che la definizione del successo avviene prima. Altrimenti, stai lanciando il dardo e poi dipingendo il punto in cui è atterrato.


1

Nel discorso Test-Driven Development: Questo non è ciò che intendevamo Steve Freeman mostra la seguente diapositiva del quadro generale del TDD (vedi immagine sotto la risposta). Ciò include un passaggio "Scrivi un test end-to-end non riuscito", seguito da "Scrivi un test unità non riuscito". (Fai clic per ingrandire, è in alto a destra)

Quindi no in TDD i test non sono sempre unit-test.

E sì, puoi (e forse dovresti) iniziare con un test end-to-end di livello superiore che fallisce prima di scrivere il primo test unitario. Questo test descrive il comportamento che si desidera ottenere. Questo genera copertura su più livelli della piramide di prova . Adrian Sutton spiega l'esperienza di LMAX, dimostrando che i test end-to-end possono svolgere un ruolo importante e importante .

inserisci qui la descrizione dell'immagine


-1

No, non può essere applicato ad altri tipi di test, per una semplice ragione pratica: altri tipi di test richiedono troppo tempo per essere eseguiti.

Tipico ciclo TDD è: scrittura test fallito, attrezzo, codice refactor. I passaggi intermedi sono la costruzione e l'esecuzione di test, che devono essere velocissimi. Se non lo sono, allora le persone inizierebbero a saltare i passaggi e quindi non farai più TDD.


1
Ciò non è corretto: a seconda del programma e del test (e della lingua), i test di integrazione end-to-end possono essere eseguiti facilmente in meno di 3 secondi. È del tutto possibile eseguire una suite completa di test end-to-end in pochissimo tempo, anche se è ben progettata. Quindi "non posso" è abbastanza forte.
Jonathan Cast

@jcast Non ho mai visto nient'altro che sia così veloce. I miei test funzionali sul mio precedente progetto sono durati 30 secondi, e questo è veloce. Integrazione ancora più a lungo. Nel mio caso, nient'altro aveva senso. Inoltre, i test unitari sono i più veloci tra tutti i tipi di test, quindi ha senso usarli.
BЈовић,
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.