Determinare cos'è un test unitario utile

44

Ho esaminato i documenti di phpunit e ho trovato la seguente citazione:

You can always write more tests. However, you will quickly find that only a fraction of the tests you can imagine are actually useful. What you want is to write tests that fail even though you think they should work, or tests that succeed even though you think they should fail. Another way to think of it is in cost/benefit terms. You want to write tests that will pay you back with information. --Erich Gamma

Mi sono chiesto. Come si determina ciò che rende un test unitario più utile di un altro, a parte ciò che è affermato in quella citazione sul rapporto costi / benefici. Come fai a decidere quale pezzo del tuo codice crei per i test unitari? Lo sto chiedendo perché un'altra di quelle citazioni diceva anche:

So if it's not about testing, what's it about? It's about figuring out what you are trying to do before you run off half-cocked to try to do it. You write a specification that nails down a small aspect of behaviour in a concise, unambiguous, and executable form. It's that simple. Does that mean you write tests? No. It means you write specifications of what your code will have to do. It means you specify the behaviour of your code ahead of time. But not far ahead of time. In fact, just before you write the code is best because that's when you have as much information at hand as you will up to that point. Like well done TDD, you work in tiny increments... specifying one small aspect of behaviour at a time, then implementing it. When you realize that it's all about specifying behaviour and not writing tests, your point of view shifts. Suddenly the idea of having a Test class for each of your production classes is ridiculously limiting. And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable. --Dave Astels

La sezione importante di questo è

*And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable. *

Quindi, se la creazione di un test per ciascun metodo è "risibile", come / quando scegli per cosa scrivere i test?

    
posta zcourts 06.07.2011 - 15:33
fonte

4 risposte

25

Quanti test per metodo?

Bene il massimo teorico e molto poco pratico è la complessità N-Path (si presuma che i test coprano tutti modi diversi attraverso il codice;)). Il minimo è UNO!. Per metodo pubblico che non esegue il test dei dettagli di implementazione, ma solo i comportamenti esterni di una classe (restituisce valori e chiama altri oggetti).

Citi:

*And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable. *

e poi chiedi:

So if creating a test for each method is 'laughable', how/when do you chose what you write tests for?

Ma penso che tu abbia frainteso l'autore qui:

L'idea di avere one test method per one method in the class to test è ciò che l'autore chiama "ridicolo".

(Per me almeno) Non si tratta di "meno", si tratta di "altro"

Quindi fammi riformulare come ho capito:

E il pensiero di testare ciascuno dei tuoi metodi con SOLO UN METODO (il suo metodo di test in una relazione 1-1) sarà ridicolo.

Per citare di nuovo il tuo preventivo:

When you realize that it's all about specifying behaviour and not writing tests, your point of view shifts.

Quando pratichi TDD, non pensare :

Ho un metodo calculateX($a, $b); e ha bisogno di un test testCalculcateX che verifica TUTTO il metodo.

Ciò che TDD ti dice è di pensare a cosa dovrebbe fare il tuo codice :

Ho bisogno di calcolare il più grande dei due valori ( primo caso di test! ) ma se $ a è minore di zero, allora dovrebbe produrre un errore ( secondo test case! ) e se $ b è minore di zero dovrebbe .... ( terzo test case ) e così via.

Vuoi testare i comportamenti, non solo i singoli metodi senza contesto.

In questo modo si ottiene una suite di test che è la documentazione per il proprio codice e REALMENTE spiega cosa si prevede di fare, forse anche perché:)

Come decidi quale parte del codice crei per i test unitari?

Bene, tutto ciò che finisce nel repository o ovunque vicino alla produzione richiede un test. Non penso che l'autore delle tue citazioni non sarebbe d'accordo con quello come ho cercato di affermare in precedenza.

Se non hai un test per questo, diventa molto più difficile (più costoso) cambiare il codice, specialmente se non stai facendo il resto.

TDD è un modo per assicurarti di avere i test per TUTTO, ma finché scrivi i test va bene. Di solito li scrivendo nello stesso giorno aiuta dal momento che non lo farai più tardi, vero? :)

Risposta ai commenti:

a decent amount of methods can't be tested within a particular context because they either depend or are dependent upon other methods

Bene, ci sono tre cose che questi metodi possono chiamare:

Metodi pubblici di altre classi

Possiamo prendere in giro altre classi così abbiamo definito lo stato lì. Abbiamo il controllo del contesto, quindi non è un problema lì.

* Metodi protetti o privati sullo stesso *

Qualsiasi cosa che non fa parte dell'API pubblica di una classe non viene testata direttamente, di solito.

Vuoi testare il comportamento e non l'implementazione e se una classe fa tutto il suo lavoro in un grande metodo pubblico o in molti più piccoli metodi protetti che vengono chiamati è implementazione . Vuoi essere in grado di CAMBIARE quei metodi protetti SENZA toccare i test. Perché i tuoi test si interromperanno se il tuo codice cambia comportamento! Ecco a cosa servono i tuoi test, per dirti quando rompi qualcosa:)

Metodi pubblici sulla stessa classe

Questo non succede molto spesso, vero? E se lo fa nel seguente esempio ci sono alcuni modi per gestirlo:

$stuff = new Stuff();
$stuff->setBla(12);
$stuff->setFoo(14);
$stuff->execute(); 

Che i setter esistono e non fanno parte della firma del metodo execute è un altro argomento;)

Quello che possiamo testare qui è se gli executes esplodono quando impostiamo i valori sbagliati. Quella setBla genera un'eccezione quando si passa una stringa può essere testata separatamente, ma se vogliamo testare che quei due valori consentiti (12 e 14) non funzionano INSIEME (per qualsiasi motivo) di quello che è un caso di test.

Se vuoi una "buona" suite di test puoi, in PHP, magari (!) aggiungere un'annotazione @covers Stuff::execute per assicurarti di generare solo la copertura del codice per questo metodo e l'altra roba che è appena impostata deve essere testato separatamente (di nuovo, se lo vuoi).

Quindi il punto è: Forse devi prima creare un po 'del mondo circostante, ma dovresti essere in grado di scrivere casi di test significativi che di solito occupano solo una o forse due funzioni reali (i setter don' conta qui). Il resto può essere deriso via etere o essere prima testato e poi invocato (vedi @depends )

* Nota: la domanda è stata migrata da SO e inizialmente riguardava PHP / PHPUnit, questo è il motivo per cui il codice di esempio ei riferimenti provengono dal mondo php, penso che questo sia applicabile anche ad altre lingue in quanto phpunit non differisce molto da altri framework di testing xUnit.

    
risposta data 06.07.2011 - 16:11
fonte
2

Test e unit test non sono le stesse cose. Il test unitario è un sottoinsieme molto importante e interessante del test complessivo. Direi che il focus di Unit Testing ci fa pensare a questo tipo di test in modi che contraddicono in qualche modo le citazioni sopra.

In primo luogo, se seguiamo il TDD o anche il DTH (cioè sviluppiamo e testiamo in stretta armonia) stiamo usando i test che scriviamo per concentrarci sul nostro progetto. Pensando ai casi d'angolo e ai test di scrittura, evitiamo di conseguenza l'insorgere dei bug, quindi in effetti scriviamo un test che ci aspettiamo di superare (OK all'inizio del TDD falliscono, ma è solo un artefatto di ordinazione, quando il codice è fatto ci aspettiamo che passino, e la maggior parte lo fa, perché hai pensato al codice.

In secondo luogo, i test delle unità sono davvero di loro stessi quando eseguiamo il refactoring. Modifichiamo la nostra implementazione ma ci aspettiamo che le risposte rimangano le stesse: il Test unitario è la nostra protezione contro la rottura del nostro contratto di interfaccia. Quindi, ancora una volta, ci aspettiamo che passino i test.

Ciò implica che per la nostra interfaccia pubblica, che presumibilmente è stabile, abbiamo bisogno di una tracciabilità chiara in modo che possiamo vedere che ogni metodo pubblico è testato.

Per rispondere alla tua domanda esplicita: Unit Test per l'interfaccia pubblica ha valore.

modificato nel commento di risposta:

Test dei metodi privati? Sì, dovremmo, ma se non dovessimo testare qualcosa, allora è lì che farei un compromesso. Dopotutto, se i metodi pubblici stanno funzionando, allora quegli errori nelle cose private potrebbero essere così importanti? Pragmaticamente, il churn tende ad accadere nelle cose private, lavori duro per mantenere la tua interfaccia pubblica, ma se le cose dipendono da cambiare le cose private possono cambiare. Ad un certo punto potremmo scoprire che mantenere i test interni è un grande sforzo. Questo sforzo è ben speso?

    
risposta data 06.07.2011 - 15:48
fonte
2

I test unitari dovrebbero far parte di una strategia di test più ampia. Seguo questi principi nella scelta di quali tipi di test scrivere e quando:

  • Concentrati sulla scrittura di test end-to-end. Copri più codice per test che con unit test e quindi ottieni più test bang per il dollaro. Rendi la convalida automatica del tuo sistema nel suo complesso.

  • Scendi giù per scrivere test di unità intorno a pepite di logica complicata. I test di unità valgono il loro peso in situazioni in cui i test end-to-end sarebbero difficili da debugare o poco complessi da scrivere per copertura del codice adeguata.

  • Attendi fino a quando l'API che stai testando non è stabile per scrivere entrambi i tipi di test. Vuoi evitare di dover refactoring sia l'implementazione che i test.

Rob Ashton ha un bel articolo su questo argomento, che ho attinto pesantemente da per articolare i principi sopra.

    
risposta data 26.03.2013 - 12:03
fonte
0

Tendo a seguire un approccio diverso al collaudo unitario che sembra funzionare bene. Invece di pensare a un test unitario come "Testare un comportamento", lo considero più "una specifica che il mio codice deve seguire". In questo modo, puoi sostanzialmente dichiarare che un oggetto dovrebbe comportarsi in un certo modo, e dato che tu pensi che altrove nel tuo programma, puoi essere abbastanza sicuro che sia relativamente privo di bug.

Se stai scrivendo un'API pubblica, questo è estremamente prezioso. Tuttavia, avrai sempre bisogno anche di una buona dose di test di integrazione end-to-end perché l'approssimazione della copertura del 100% delle unità di misura non vale la pena e mancherà a cose che la maggior parte considererebbero "non testabili" dai metodi di test unitari (beffardo, etc)

    
risposta data 26.03.2013 - 19:30
fonte

Leggi altre domande sui tag