È normale spendere tanto, se non di più, i test di scrittura del tempo rispetto al codice effettivo?

203

Trovo che i test siano molto più difficili da scrivere rispetto al codice effettivo che stanno testando. Non è insolito per me dedicare più tempo alla scrittura del test rispetto al codice che sta testando.

È normale o sto facendo qualcosa di sbagliato?

Le domande " Is testing unit o sviluppo test-driven vale la pena? "," Stiamo spendendo più tempo nell'implementare test funzionali che implementare il sistema stesso, è normale? " e le loro risposte sono maggiori sul fatto che il test valga la pena (come in "dovremmo saltare del tutto test di scrittura?") . Mentre sono convinto che i test siano importanti, mi chiedo se passare più tempo a test rispetto al codice effettivo è normale o se sono solo io.

A giudicare dal numero di visualizzazioni, risposte e upvotes della mia domanda ricevuta, posso solo presumere che sia una preoccupazione legittima che non viene affrontata in nessun'altra domanda sul sito web.

    
posta springloaded 14.10.2015 - 00:27
fonte

9 risposte

198

Ricordo che da un corso di ingegneria del software, uno spende circa il 10% del tempo di sviluppo per scrivere un nuovo codice, e l'altro 90% è il debug, il test e la documentazione.

Dato che i test unitari acquisiscono il debugging e testano lo sforzo in un codice (potenzialmente automatizzabile), sarebbe logico che vi sia un maggiore impegno in essi; il tempo effettivo impiegato non dovrebbe essere molto più del debug e del test che si farebbe senza scrivere i test.

Infine, anche i test dovrebbero raddoppiare come documentazione! Uno dovrebbe scrivere unit test nel modo in cui il codice è destinato ad essere utilizzato; i test (e l'uso) dovrebbero essere semplici, mettere le cose complicate nell'implementazione.

Se i tuoi test sono difficili da scrivere, il codice che testano è probabilmente difficile da usare!

    
risposta data 14.10.2015 - 01:16
fonte
95

Lo è.

Anche se si eseguono solo test delle unità, non è insolito avere più codice all'interno dei test rispetto al codice effettivamente testato. Non c'è niente di sbagliato in questo.

Considera un semplice codice:

public void SayHello(string personName)
{
    if (personName == null) throw new NullArgumentException("personName");

    Console.WriteLine("Hello, {0}!", personName);
}

Quali sarebbero i test? Ci sono almeno quattro semplici casi da testare qui:

  1. Il nome della persona è null . L'eccezione è effettivamente lanciata? Sono almeno tre righe di codice di test da scrivere.

  2. Il nome della persona è "Jeff" . Riceviamo "Hello, Jeff!" in risposta? Ecco quattro righe di codice di test.

  3. Il nome della persona è una stringa vuota. Quale output ci aspettiamo? Qual è l'uscita effettiva? Domanda laterale: soddisfa i requisiti funzionali? Ciò significa altre quattro righe di codice per il test dell'unità.

  4. Il nome della persona è abbastanza corto per una stringa, ma troppo lungo per essere combinato con "Hello, " e il punto esclamativo. Cosa succede? ¹

Questo richiede molto codice di test. Inoltre, i pezzi più elementari di codice spesso richiedono un codice di configurazione che inizializza gli oggetti necessari per il codice in prova, che spesso porta anche a scrivere stub e mock, ecc.

Se il rapporto è molto grande, nel qual caso puoi verificare alcune cose:

  • Esiste la duplicazione del codice tra i test? Il fatto che si tratti di un codice di prova non significa che il codice debba essere duplicato (incollato) tra test simili: tale duplicazione renderà difficile il mantenimento di tali test.

  • Ci sono test ridondanti? Come regola generale, se si rimuove un test unitario, la copertura del ramo dovrebbe diminuire. In caso contrario, potrebbe indicare che il test non è necessario, poiché i percorsi sono già coperti da altri test.

  • Stai testando solo il codice che dovresti testare? Non ci si aspetta che test il framework sottostante delle librerie di terze parti, ma esclusivamente il codice del progetto stesso .

Con test fumo, test di integrazione e di sistema, test funzionali e di accettazione e prove di stress e carico, aggiungi ancora più codice di test, quindi avere quattro o cinque LOC di test per ogni LOC del codice reale non è qualcosa che dovresti preoccuparti circa.

Una nota su TDD

Se sei preoccupato per il tempo necessario per testare il tuo codice, potrebbe essere che tu stia sbagliando, cioè prima il codice, i test più tardi. In questo caso, TDD può aiutarti incoraggiandoti a lavorare in iterazioni di 15-45 secondi, passando da codice a test. Secondo i sostenitori di TDD, accelera il processo di sviluppo riducendo sia il numero di test che devi fare e, soprattutto, la quantità di codice aziendale da scrivere e specialmente riscrivi per i test.

¹ Lascia n essere la lunghezza massima di una stringa . Possiamo chiamare SayHello e passare per riferimento una stringa di lunghezza n - 1 che dovrebbe funzionare bene. Ora, al passo Console.WriteLine , la formattazione dovrebbe finire con una stringa di lunghezza n + 8, che si tradurrà in un'eccezione. Probabilmente, a causa dei limiti di memoria, anche una stringa contenente n / 2 caratteri causerà un'eccezione. La domanda che dovremmo porre è se questo quarto test sia un test unitario (sembra uno, ma può avere un impatto molto più alto in termini di risorse rispetto ai test unitari medi) e se verifica il codice effettivo o il framework sottostante.

    
risposta data 14.10.2015 - 00:49
fonte
58

Penso che sia importante distinguere tra due tipi di strategie di test: test unitario e test di integrazione / accettazione.

Sebbene i test unitari siano necessari in alcuni casi, è spesso irrimediabilmente superato. Ciò è esacerbato da metriche prive di significato imposte agli sviluppatori, come "copertura del 100%". link fornisce un argomento convincente per questo. Prendi in considerazione i seguenti problemi con test unitari aggressivi:

  • Un'abbondanza di test inutili che non documentano un valore aziendale, ma semplicemente esistono per avvicinarsi a quella copertura al 100%. Dove lavoro, dobbiamo scrivere test unitari per fabbriche che non fa altro che creare nuove istanze di classi. Non aggiunge valore. O quei lunghi metodi .equals () generati da Eclipse - non è necessario testarli.
  • Per facilitare i test, gli sviluppatori suddividono gli algoritmi complessi in unità testabili più piccole. Sembra una vittoria, giusto? Non se è necessario avere 12 classi aperte per seguire un percorso di codice comune. In questi casi, il test dell'unità può effettivamente ridurre la leggibilità del codice. Un altro problema con questo è che se si taglia il proprio codice in pezzi troppo piccoli, si finisce con una grandezza di classi (o pezzi di codice) che sembrano non avere giustificazione oltre ad essere un sottoinsieme di un altro pezzo di codice.
  • Il refactoring del codice altamente coperto può essere difficile, in quanto è anche necessario mantenere l'abbondanza di test unitari che dipendono dal fatto che funziona proprio così . Ciò è esacerbato dai test unitari comportamentali, in cui parte del test verifica anche l'interazione dei collaboratori di una classe (di solito derisi).

Il test di integrazione / accettazione, d'altro canto, è una parte estremamente importante della qualità del software e, nella mia esperienza, dovresti dedicare una notevole quantità di tempo a correggerli correttamente.

Molti negozi hanno bevuto il kool-aiuto TDD, ma come mostra il link qui sopra, una serie di studi mostra che il suo vantaggio è inconcludente.

    
risposta data 14.10.2015 - 09:58
fonte
10

Non può essere generalizzato.

Se ho bisogno di implementare una formula o un algoritmo da rendering basato fisicamente, può essere che trascorro 10 ore su test di unità paranoidi, poiché so che il minimo bug o imprecisione può portare a bachi che sono quasi impossibili da diagnosticare , mesi dopo.

Se voglio solo raggruppare logicamente alcune linee di codice e dargli un nome, usato solo in ambito file, non posso testarlo affatto (se insisti a scrivere test per ogni singola funzione, senza eccezioni, i programmatori potrebbe tornare a scrivere il minor numero possibile di funzioni).

    
risposta data 14.10.2015 - 10:38
fonte
3

Sì, è normale che tu stia parlando di TDDing. Quando si eseguono test automatici, si protegge il comportamento desiderato del codice. Quando scrivi prima i test, determini se il codice esistente ha già il comportamento che desideri.

Questo significa che:

  • Se scrivi un test che fallisce, allora correggere il codice con la cosa più semplice che funziona è più breve della scrittura del test.
  • Se scrivi un test che passa, non hai più codice da scrivere, spendendo più tempo a scrivere test che a codice.

(Questo non tiene conto del refactoring del codice, che mira a dedicare meno tempo alla scrittura del codice successivo. È bilanciato dal refactoring del test, che mira a dedicare meno tempo alla scrittura dei test successivi.)

Sì, anche se stai parlando di scrivere test dopo il fatto, allora passerai più tempo:

  • Determinazione del comportamento desiderato.
  • Determinazione dei metodi per testare il comportamento desiderato.
  • Soddisfare le dipendenze del codice per poter scrivere i test.
  • Correzione del codice per i test non riusciti.

Di quanto spenderai effettivamente scrivendo codice.

Quindi sì, è una misura prevista.

    
risposta data 14.10.2015 - 10:37
fonte
3

Trovo che sia la parte più importante.

Il test delle unità non riguarda sempre il "vedere se funziona correttamente", ma l'apprendimento. Una volta provato qualcosa, diventa "hardcoded" nel tuo cervello e alla fine puoi ridurre il tempo di test unitario e puoi trovare te stesso a scrivere classi e metodi completi senza testare nulla fino a quando non lo hai fatto.

Ecco perché una delle altre risposte in questa pagina dice che in un "corso" hanno fatto il test al 90%, perché tutti avevano bisogno di imparare gli avvertimenti sul loro obiettivo.

Il test unitario non è solo un uso molto prezioso del tuo tempo in quanto migliora letteralmente le tue abilità, è un buon modo per ripassare il tuo codice e trovare un errore logico lungo la strada.

    
risposta data 14.10.2015 - 06:16
fonte
2

Può essere per molte persone, ma dipende.

Se stai scrivendo i test per primi (TDD), potresti riscontrare qualche sovrapposizione nel tempo impiegato per scrivere il test in realtà è utile per scrivere il codice. Prendere in considerazione:

  • Determinazione di risultati e input (parametri)
  • Convenzioni sui nomi
  • Struttura - dove mettere le cose.
  • Semplicissimo pensiero

Quando scrivi dei test dopo aver scritto il codice, potresti scoprire che il tuo codice non è facilmente verificabile, quindi scrivere test è più difficile / richiede più tempo.

La maggior parte dei programmatori ha scritto codice molto più a lungo dei test, quindi mi aspetterei che la maggior parte di loro non sia così fluente. Inoltre, è possibile aggiungere il tempo necessario per comprendere e utilizzare il framework di test.

Penso che abbiamo bisogno di cambiare la nostra mentalità su quanto tempo ci vuole per il codice e su come è coinvolto il test unitario. Non guardarlo mai a breve termine e mai e poi mai confrontare il tempo totale per fornire una caratteristica particolare perché devi considerare non solo stai scrivendo il codice migliore / meno buggato, ma il codice che è più facile da cambiare e lo rende ancora meglio / meno bug.

Ad un certo punto, siamo tutti in grado di scrivere codice che è così buono, quindi alcuni degli strumenti e delle tecniche possono offrire così tanto per migliorare le nostre capacità. Non è che potrei costruire una casa se avessi solo una sega a laser.

    
risposta data 14.10.2015 - 01:13
fonte
2

Is it normal to spend as much, if not more, time writing tests than actual code?

Sì, lo è. Con alcuni avvertimenti.

Prima di tutto, è "normale" nel senso che la maggior parte dei grandi negozi funziona in questo modo, quindi anche se in questo modo fosse completamente fuorviato e stupido, tuttavia, il fatto che la maggior parte dei grandi negozi funzionino in questo modo lo rende "normale" .

Con ciò non intendo implicare che testare sia sbagliato. Ho lavorato in ambienti senza test, e in ambienti con test ossessivo-compulsivi, e posso ancora dirti che anche il test ossessivo-compulsivo è stato meglio di nessun test.

E non faccio ancora TDD, (chissà, potrei in futuro,) ma faccio la maggior parte dei miei cicli edit-run-debug eseguendo i test, non l'applicazione vera e propria, quindi naturalmente lavoro molto sui miei test, in modo da evitare il più possibile di dover eseguire l'applicazione effettiva.

Tuttavia, tieni presente che ci sono pericoli in test eccessivi, e in particolare nella quantità di tempo speso per mantenere i test. (In primo luogo sto scrivendo questa risposta per specificarlo in modo specifico.)

Nella prefazione di The Art of Unit Testing di Roy Osherove (Manning, 2009) l'autore ammette di aver partecipato a un progetto che non è riuscito in gran parte a causa dell'enorme onere di sviluppo imposto da test unitari mal progettati che dovevano essere mantenuti per tutta la durata dello sforzo di sviluppo. Quindi, se ti ritrovi a passare troppo tempo a non fare altro che mantenere i tuoi test, questo non significa necessariamente che sei sul retta via, perché è "normale". Il tuo sforzo di sviluppo potrebbe essere entrato in una modalità non salutare, dove un ripensamento radicale della tua metodologia di test potrebbe essere necessario per salvare il progetto.

    
risposta data 06.12.2015 - 17:47
fonte
0

Is it normal to spend as much, if not more, time writing tests than actual code?

  • per scrivere unit test (testare un modulo in isolamento) se il codice è altamente accoppiato (legacy, non abbastanza separazione delle preoccupazioni , mancante iniezione di dipendenza , non tdd sviluppato )
  • per scrivere integrazione / test di accettazione se la logica da testare è raggiungibile solo tramite gui-code
  • No per scrivere test di integrazione / accettazione purché il codice gui e la logica aziendale siano separati (il test non ha bisogno di interagire con il gui)
  • No per scrivere test di unità se esiste il separatore di dubbi, l'iniezione di dipendenza, il codice era testdriven sviluppato (tdd)
risposta data 14.10.2015 - 10:36
fonte

Leggi altre domande sui tag