Il mio test unitario sembra molto simile alla classe che sto testando. Come sostengo la mia posizione secondo cui il test è, in effetti, corretto?

4

Sto lavorando a un componente standard che dovrebbe generare una sorta di struttura di progetto "come iniziare". Pensa, in un contesto di app web, a un componente che genererà un controller, una vista e la configurazione dei percorsi in modo che, una volta chiamato, puoi andare immediatamente al browser, digitare il percorso e ottenere l'output .

In questa norma, una delle mie classi ha la responsabilità di fornire il contenuto di ogni file che genera (il contenuto del file del controller, il contenuto del file di rotte, il contenuto del view fie, ecc.)

Con una tale responsabilità, un metodo dovrebbe restituire il contenuto foo , il test dovrebbe verificare che, in effetti, viene restituito "pippo".

Finisce in questo modo:

// SUT
function getFooFileContent() {
    return 'foo';
}

// test
function testGetFooFileContent() {
    string expected = 'foo';

    string result = sut.getFooFileContent();

    assertEquals(expected, result);
}

Le persone a cui sto spiegando il motivo di questo test sono nuove per i test unitari, quindi è normale che non capiscano a prima vista perché è OK, e dubitano che questo test abbia effettivamente un valore per cominciare.

Dato che sono incaricato di introdurre i test di unità per il mio team, voglio spiegarlo con risorse che spiegano questo "tipo" di test in particolare, in modo da avere una discussione solida piuttosto che chiedere loro di accetta quel tipo di test.

PS: Per la cronaca, quando ho iniziato con i test unitari (autodidatta, nessun mentore purtroppo), avevo lo stesso dubbio. Nel mio caso ho appena accettato il test e ho capito il suo valore nel corso degli anni, quando ho potuto finalmente vedere il fatto che i test dovrebbero legare la funzionalità alle specifiche. È difficile vederlo quando stai iniziando.

Aggiornamento 1:

A causa della mia domanda contrassegnata come duplicata per il comune dovrei testare un getter / setter? dico:

Questo non è davvero un getter e la domanda non riguarda se ne valga la pena o meno. La specifica della classe è di fornire un modello di testo per un piano d'appoggio. L'implementazione potrebbe benissimo evolvere nel leggere il testo da un file non * .php e, in tal caso, un test avrebbe valore in quanto assocerebbe le specifiche alla funzionalità.

La domanda è come spiegare il fatto che, anche con l'implementazione corrente (quella che sta appena restituendo un blocco di stringa) il test stesso è corretto. Sto specificatamente chiedendo risorse per il backup di quell'argomento.

    
posta Christopher Francisco 26.10.2016 - 01:28
fonte

2 risposte

11

I test dovrebbero avere un punto.

Non scrivere mai test semplicemente perché devi scrivere test. Questo è il modo di pensare cerimoniale. Le cerimonie sono una scusa per spegnere il cervello. Non è sempre meglio avere test piuttosto che solo codice. A volte è peggio.

Non sono un odiatore TDD. L'ho fatto professionalmente. Vorrei non aver mai dovuto lavorare su nessun progetto che non avesse prove ben scritte.

Ma ho visto i test obbligatori bloccare il codice nella pietra perché i test esistevano senza una ragione migliore di quella che avrebbero dovuto esistere. I test dovrebbero avere una buona ragione per esistere.

Esistono alcuni test per semplificare il processo di refactoring.
Esistono alcuni test per semplificare l'aggiunta di funzioni.
Esistono alcuni test per semplificare il debug.
Esistono alcuni test per rendere più semplice la conformità a una specifica.
Esistono alcuni test per rendere più semplice soddisfare le esigenze dei clienti.
Esistono alcuni test per rendere più facile individuare le modifiche indesiderate.
Esistono alcuni test per far sembrare che tu scriva dei test, proprio come i ragazzi fantastici.

Quindi chiedo, qual è il punto di questo test?

Nel tuo commento :

I thought about forcing a regression bug, but given the fact that the tests I've covered so far are unit, and not integration, I don't see how to explain it to them with mere 'templates'. – Christopher Francisco

hai menzionato i test di regressione come se fosse sempre stato fatto solo con test di integrazione e non con test unitari. Questo non è vero. In effetti, i test di regressione sono l'unica scusa a cui riesco a pensare di scrivere un test come quello che hai fatto qui. Questo è un test unitario. Ciò che renderebbe anche un test di regressione è eseguirlo anche quando non pensi di aver apportato modifiche a questa unità.

Questa funzione getFooFileContent() ha un comportamento banalmente semplice. Non vedo nessuna logica da testare, nessun caso da esplorare, nessun potenziale bug da scoprire. Un buon poster di prova non è adatto a questa funzione.

Un test di regressione riguarda l'unico punto che posso vedere qui. Questo fa sorgere la domanda sul perché vuoi fare un test di regressione.

Una buona storia per giustificare un test di regressione è che vuoi essere avvisato se il comportamento cambia. Questa storia è una dura vendita a programmatori di manutenzione a tempo limitato che desiderano che le modifiche siano più facili da rendere non più difficili. Quindi vendi il punto che facilita il debugging. Se in qualche modo il comportamento cambia, viene individuato prima della spedizione.

Bene, perché questo cambierebbe? Ho sentito la tua squadra chiedere. Perché qualcuno lo ha cambiato, ovviamente. Bene, e se volessero? Quello che sta succedendo qui è che i cambiamenti stanno diventando costosi perché ora non posso aggiornare questa cosa senza aggiornare il test. Questo è un costo e deve essere giustificato.

Dire che qualcuno stava scorrendo e senza notare che ha evidenziato un po 'di questa funzione, poi ha urtato la barra spaziatrice e ora quel bit è sparito. Questa modifica inosservata riesce a compilare e quindi sta per uscire la prossima volta che spediamo.

Sì, è inverosimile. Ma potrebbe succedere. Se lo facesse, quanto costerebbe?

Beh potrebbe mandare in bancarotta la compagnia. Un po 'difficile da dire da questo esempio. Questo è il vero problema. Devi raccontare la storia del test dimostrando che vale la pena.

Tieni presente che usi il controllo del codice sorgente (hai ragione?) quindi nulla è stato perso per sempre. Tutto ciò ti sta comprando è conoscere il problema prima piuttosto che dopo.

Un test di comportamento banale ha un costo. Non testare un comportamento banale ha un costo. Qui è dove devi trovare l'equilibrio. Troverai quell'equilibrio solo quando sai perché stai scrivendo il test. Assicurati di conoscere la storia che renderebbe utile questo test.

Puoi provare a testare tutto ciò che potrebbe rompersi, ma mi accontento di scrivere ogni test che faciliti il lavoro.

    
risposta data 26.10.2016 - 06:08
fonte
3

Se stavo testando un generatore di modelli di template, allora non scriverei un test per quel getter; quello che vorrei fare è scrivere un test che esegue l'intero generatore e confronta i file creati con una raccolta di file statici che è l'output atteso. Questo è un test completamente sufficiente per tutte le parti del generatore che non hanno una logica interessante. Nei casi in cui è logica interessante, presumibilmente ci sarà un qualche tipo di parametri di input e tu vorrai scrivere test unitari che coprano i vari parametri, che non avranno il tuo problema "semplicemente ripetendo il codice sotto test".

Alcuni sostengono che questo non è un test unitario, ma un test di integrazione. Beh, abbastanza equo, ma il mio punto di vista è che è più importante che l'intero sistema sia validamente testato rispetto a quello dei test di una classificazione specifica. Consideriamo i vantaggi di un test unitario su un test di integrazione:

  1. Il test richiede meno impostazioni ed è quindi molto più veloce.
  2. È più facile trovare la causa di un errore perché c'è meno codice sotto test che potrebbe averlo causato.
  3. È più facile (o possibile) verificare i casi limite perché non devi trovare una configurazione o un input per l'intero sistema che esercita il caso specifico in una particolare classe.

In questo caso, suppongo che (1) sia improbabile che sia un problema, (3) non si applica perché il codice è banale e (2) non si applica perché l'origine della stringa errata è ovvia - se non sai già che getFooFileContent è coinvolto, solo la ricerca del codice per la stringa di output errata troverà la stringa letterale di origine.

E facendo in questo modo, i refactoring di come viene generato il contenuto del file foo che rimuovono / riposizionano quel metodo non ti costringono a dover cambiare i test, il che significa che è facile vedere che la modifica è un refactoring puro poiché i test passati prima, passati dopo e non modificati.

    
risposta data 26.10.2016 - 20:17
fonte

Leggi altre domande sui tag