In che modo le asserzioni e altri test di eliminazione degli errori si inseriscono in TDD?

6

link

You are not allowed to write any production code unless it is to make a failing unit test pass.

Quindi dove si inserisce in questo? Se hai seguito questa regola, non avresti mai scritto un'affermazione senza un test unitario. Ma come metteresti alla prova l'esistenza di una asserzione? La natura stessa di assert sembra rendere quest'idea impossibile poiché il test esploderebbe.

E il codice significava gestire condizioni assurde che sono difficili o impossibili da replicare? Una sorta di FUBAR da una chiamata di sistema, ad esempio ... È necessario gestire l'errore ma la condizione che causa l'errore è piuttosto rara e proviene dal sistema operativo. Come si scrive un test unitario per fallire?

O dici che stai scrivendo una libreria di modelli e vuoi fare alcuni controlli di concetto prima di andare troppo in profondità nella terra del vomito modello? Non conosco alcun framework che supporti l'errore di compilazione come PASS di test.

Ho provato a vivere secondo alcuni di questi editti del TDD, ma ad un certo punto ho sempre incontrato muri che non posso aggirare. Questi sono alcuni.

Credo che dovrei dire che sto parlando di asserire nel senso C / C ++ in cui un programma, costruito senza NDEBUG, fallirà se l'asserzione fallirà. Non un meccanismo di lancio di eccezioni chiamato chiamato . Non pensavo nemmeno che qualcuno avrebbe usato quel nome per lanciare eccezioni.

    
posta Crazy Eddie 19.05.2012 - 01:14
fonte

4 risposte

14

Mi sembra che tu stia sperimentando dissonanza cognitiva , cercando di credere a due idee contraddittorie e accettandole entrambe come valide . Il modo per risolverlo è capire che uno (o forse entrambi) deve essere errato e scoprire quale è. In questo caso, il problema è che quegli editti sono basati su una premessa sbagliata, che lo zio Bob ripete diverse volte più avanti:

However, think about what would happen if you walked in a room full of people working this way. Pick any random person at any random time. A minute ago, all their code worked.

Let me repeat that: A minute ago all their code worked! And it doesn't matter who you pick, and it doesn't matter when you pick. A minute ago all their code worked!

Questa è la promessa brillante di TDD: prova tutto, fallo passare tutti i test e tutto il tuo codice funzionerà.

Il problema è, questa è una palese menzogna.

Verifica tutto, fai in modo che passino tutti i tuoi test e passeranno tutti i tuoi test, niente di più, niente di meno. Ciò non significa nulla di particolarmente utile; significa solo che nessuna delle condizioni di errore che hai pensato di testare esiste nella base di codice. (Ma se pensavate di testare per loro, allora avete prestato abbastanza attenzione a quella possibilità di scrivere il codice abbastanza attentamente da farlo subito, quindi è meno utile di quanto potrebbe essere.)

Ciò non significa che qualsiasi errore che non abbia mai pensato non sia presente nella base di codice. Inoltre, non significa che i tuoi test, che sono anche dei codici scritti da te, sono privi di bug. (Porta questo concetto alla sua logica conclusione e finisci colto in una ricorsione infinita. I test sono finiti. )

Per fare un esempio, c'è una libreria di scripting open source che uso il cui autore vanta una copertura del test unitaria superiore al 90% e una copertura del 100% in tutte le funzionalità di base. Ma il tracker dei problemi ha quasi 300 bug e continuano a venire. Penso di aver trovato cinque dei primi giorni di utilizzo nei compiti del mondo reale. (A suo merito, l'autore li ha risolti molto velocemente, e nel complesso è una libreria di buona qualità, ma ciò non cambia il fatto che i suoi test unitari "al 100%" non hanno riscontrato questi problemi, che si sono presentati quasi immediatamente in uso effettivo.)

L'altro problema principale è che, mentre prosegui,

every hour you are producing several tests. Every day dozens of tests. Every month hundreds of tests. Over the course of a year you will write thousands of tests.

... e poi i tuoi requisiti cambiano. Devi implementare una nuova funzione o modificarne una esistente e quindi rompi il 10% dei tuoi test unitari, e devi esaminarli manualmente tutti per discernere quali sono stati interrotti perché hai commesso un errore e quali sono stati interrotti perché i test stessi non testano più il comportamento corretto. E il 10% di migliaia di test è un lavoro extra non necessario. (Soprattutto se lo fai 1 test alla volta, come richiesto dai tre editti!)

Quando ci pensi, questo rende il test unitario molto simile alle variabili globali, o molti altri "schemi" di cattivo design: può sembrare utile e ti fa risparmiare tempo e fatica, ma non ti accorgi dei disastrosi costa fino a quando il tuo progetto diventa abbastanza grande che il loro effetto complessivo è disastroso, e ormai è troppo tardi.

It is now two decades since it was pointed out that program testing may convincingly demonstrate the presence of bugs, but can never demonstrate their absence. After quoting this well-publicized remark devoutly, the software engineer returns to the order of the day and continues to refine his testing strategies, just like the alchemist of yore, who continued to refine his chrysocosmic purifications.

-- Edsger W. Djikstra. (Written in 1988, so it's now closer to 4.5 decades.)

    
risposta data 19.05.2012 - 01:47
fonte
7

So where do asserts fit into this?

Verifica il comportamento del tuo codice

Se hai bisogno di testare che un asser fires che farlo. Passa nella variabile che genera l'eccezione e l'eccezione e nel tuo test intercetta l'eccezione per affermare che è stata lanciata.

And what about code meant to handle absurd conditions that are difficult or impossible to replicate?

Hai alcune scelte.

Non testare

Hai la possibilità di non provare. I test forniscono una misura di mitigazione del rischio. Puoi scegliere di non testare, ma renditi conto che aumenti il rischio.

Usa i mazzi per facilitare la replica

Puoi quasi sempre prendere in giro alcune chiamate sottostanti per lanciare l'eccezione appropriata o la condizione di errore che può essere testata.

Test indiretto

Di solito vuoi testare una condizione direttamente. A volte è troppo difficile o costoso. In alcuni casi è possibile attenuare parte del rischio testando indirettamente le condizioni. Ciò includerebbe il test per stati iniziali, effetti collaterali o altri indicatori della condizione.

Eccezioni alla regola

Alcuni casi saranno sempre molto difficili da testare direttamente. Test per un deadlock, casi di inattività delle risorse, i problemi di threading possono a volte essere difficili.

    
risposta data 20.05.2012 - 05:50
fonte
4

Non sei sicuro del motivo per cui dici che "il test esploderebbe". In questo esempio sto testando un'affermazione:

public int methodUnderTest() {
    int i = resultOfACalculationThatShouldNeverBeZero();
    assert (i != 0);
    return 5 / i;
}

private int resultOfACalculationThatShouldNeverBeZero() {
    return 0;
}

@Test(expected = AssertionError.class)
public void shouldAssertBeforeDividingByZero() {
    methodUnderTest();
}

In uno scenario del mondo reale il resultOfACalculationThatShouldNeverBeZero dovrebbe essere deriso in qualche modo.

Detto ciò, non credo che le asserzioni (almeno in Java) siano troppo utili. È più simile a una stampella durante il debug, per convalidare le proprie assunzioni, ma probabilmente indica un metodo troppo complesso. Un punto dell'affermazione è di documentare l'intento. Anche un test unitario lo fa.

Detto questo, prenderei quello che dice lo zio Bob con un granello di sale. Ho letto Clean Code e Agile Software Development e quei libri contengono un sacco di codice molto pessimo (che infrange quasi tutte le sue regole) ed esempi in cui in realtà rende il codice "fisso" peggio rispetto al originale ..

    
risposta data 19.05.2012 - 02:02
fonte
1

Uso test e asserzioni per due scopi diversi.

Uso i test principalmente per comunicare il comportamento desiderato del mio programma e secondariamente per convalidare detto comportamento. Uso asserzioni principalmente per comunicare ipotesi con potenziali insidie e secondariamente per convalidare dette ipotesi.

Nemmeno i proiettili d'argento, e nella mia esperienza funzionano entrambi meglio se visti come uno strumento per comunicare l'intento ad altri programmatori, poiché ritengo che questo sia ciò a cui sono abili. Come altri hanno già detto, non farti intrappolare nel dogma. Scopri quale valore offrono gli strumenti e il tuo team e aggiungili al tuo processo se necessario.

    
risposta data 22.05.2012 - 08:58
fonte

Leggi altre domande sui tag