Come migliorare nel testare il proprio codice

45

Sono uno sviluppatore di software relativamente nuovo, e una delle cose che penso che dovrei migliorare è la mia capacità di testare il mio codice. Ogni volta che sviluppo una nuova funzionalità, trovo molto difficile seguire tutti i percorsi possibili in modo da trovare bug. Tendo a seguire il percorso dove tutto funziona. So che questo è un problema ben noto che i programmatori hanno, ma non abbiamo tester al mio attuale datore di lavoro e i miei colleghi sembrano essere piuttosto bravi in questo.

Nella mia organizzazione, non facciamo né test di sviluppo né test unitari. Mi aiuterebbe molto ma non è probabile che questo cambierà.

Cosa pensate che potrei fare per superare questo problema? Quale approccio utilizzi per testare il tuo codice?

    
posta Fran Sevillano 29.08.2011 - 14:53
fonte

14 risposte

20

Il lavoro di un programmatore è costruire cose.

Il lavoro di un tester è quello di rompere le cose.

Il più difficile è rompere le cose che hai appena costruito. Avrai successo solo superando questa barriera psicologica.

    
risposta data 29.08.2011 - 15:00
fonte
14

Franciso, ho intenzione di fare alcune ipotesi qui, sulla base di quello che hai detto:

"Non eseguiamo né TDD né test di unità. Mi aiuterebbe molto ma non è probabile che cambierà".

Da ciò, sospetto che la tua squadra non attribuisca molto valore ai test o che la gestione non spinga il budget per il team a cercare di riordinare il codice esistente e mantenere debito tecnico al minimo.

In primo luogo, devi convincere la tua squadra / gestione del valore del test. Sii diplomatico. Se la gestione sta portando avanti la tua squadra, devi mostrare loro alcuni fatti, come ad esempio il tasso di difetti per ogni versione. Il tempo speso per correggere i difetti potrebbe essere speso meglio su altre cose, come migliorare l'applicazione e renderla più adattabile ai requisiti futuri.

Se il team e il management in generale sono apatici nel correggere il codice e non si sente soddisfatto, potrebbe essere necessario cercare un altro posto dove lavorare, a meno che non si riesca a convincerli come ho detto. Ho riscontrato questo problema a vari livelli in tutti i luoghi in cui ho lavorato. Potrebbe essere qualsiasi cosa, dalla mancanza di un modello di dominio appropriato, a una scarsa comunicazione nel team.

La cura del tuo codice e la qualità del prodotto che sviluppi è un buon attributo e uno che desideri incoraggiare sempre in altre persone.

    
risposta data 29.08.2011 - 15:17
fonte
11

Se esegui il codice in C, Objective-C o C ++ puoi usare l' CLang Static Analyzer per criticare la tua fonte senza effettivamente eseguendolo.

Sono disponibili alcuni strumenti di debug della memoria: ValGrind, Guard Malloc su Mac OS X, Electric Fence su * NIX.

Alcuni ambienti di sviluppo offrono la possibilità di utilizzare un allocatore di memoria di debugging, che riempie le pagine appena assegnate e le pagine appena liberate con garbage, rileva la liberazione di puntatori non assegnati e scrive alcuni dati prima e dopo ciascun blocco heap, con il debugger viene chiamato se il modello conosciuto di tali dati cambia mai.

Qualche tizio su Slashdot ha detto di avere un sacco di valore dal single-stepping di sempre nuove linee di sorgente in un debugger. "È così", ha detto. Non seguo sempre il suo consiglio, ma quando lo ho è stato molto utile per me. Anche se non si dispone di un test case che stimola un percorso di codice non comune, è possibile spostare una variabile nel debugger per intraprendere tali percorsi, ad esempio allocando memoria, quindi utilizzare il debugger per impostare il nuovo puntatore su NULL anziché su indirizzo di memoria, quindi passando attraverso il gestore degli errori di allocazione.

Usa asserzioni - la macro assert () in C, C ++ e Objective-C. Se la tua lingua non fornisce una funzione di affermazione, scriverne una tu stesso.

L'uso asserisce generosamente, quindi lascialo nel tuo codice. Chiamo assert () "Il test che continua a testare". Li uso più comunemente per controllare le precondizioni al punto di ingresso della maggior parte delle mie funzioni. Questa è una parte di "Programming by Contract", che è integrata nel linguaggio di programmazione Eiffel. L'altra parte è postcondizioni, ovvero utilizzando assert () ai punti di ritorno della funzione, ma trovo che non ne traggo il maggior numero possibile come precondizioni.

Puoi anche usare assert per controllare gli invarianti di classe. Mentre nessuna classe è strettamente necessaria per avere alcuna invariante, la maggior parte delle classi progettate in modo sensibile le ha. Un invariante di classe è una condizione che è sempre vera oltre che all'interno delle funzioni membro che potrebbero collocare temporaneamente l'oggetto in uno stato incoerente. Tali funzioni devono sempre ripristinare la coerenza prima che ritornino.

Quindi ogni funzione membro poteva controllare l'invariante all'entrata e all'uscita e la classe poteva definire una funzione chiamata CheckInvariant che qualsiasi altro codice poteva chiamare in qualsiasi momento.

Utilizza uno strumento di copertura del codice per verificare quali linee della tua fonte vengono effettivamente testate, quindi progetta test che stimolino le linee non testate. Ad esempio, puoi controllare i gestori di memoria bassa eseguendo la tua app all'interno di una VM configurata con poca memoria fisica e nessun file di scambio o molto piccolo.

(Per qualche ragione non sono mai stato a conoscenza, mentre BeOS poteva girare senza file di swap, era altamente instabile in quel modo. Dominic Giampaolo, che ha scritto il file system BFS, mi ha spinto a non eseguire mai il BeOS senza swap. non capisco perché dovrebbe essere importante, ma deve essere stata una sorta di artefatto di implementazione.)

Dovresti anche testare la risposta del tuo codice agli errori di I / O. Prova a memorizzare tutti i tuoi file su una condivisione di rete, quindi disconnetti il cavo di rete mentre l'app ha un carico di lavoro elevato. Analogamente, scollega il cavo o disattiva la rete wireless, se comunichi in una rete.

Una cosa che trovo particolarmente irritante sono i siti web che non hanno un robusto codice Javascript. Le pagine di Facebook caricano dozzine di piccoli file Javascript, ma se uno di essi non riesce a scaricare, l'intera pagina si interrompe. Ci deve essere solo un modo per fornire una certa tolleranza d'errore, ad esempio riprovando un download o per fornire una sorta di fallback ragionevole quando alcuni dei tuoi script non sono stati scaricati.

Prova ad uccidere la tua app con il debugger o con "kill -9" su * NIX mentre è nel bel mezzo della scrittura di un file grande e importante. Se la tua app è ben architettata, l'intero file verrà scritto o non verrà scritto affatto, o forse se è solo parzialmente scritto, ciò che viene scritto non sarà corrotto, con quali dati vengono salvati essendo completamente utilizzabili da l'app dopo la rilettura del file.

I database

hanno sempre I / O disco con tolleranza d'errore, ma praticamente nessun altro tipo di app. Mentre i filesystem journaled prevengono il danneggiamento del filesystem in caso di blackout o interruzioni dell'alimentazione, non fanno nulla per impedire il danneggiamento o la perdita dei dati dell'utente finale. Questa è la responsabilità delle applicazioni utente, ma difficilmente altri oltre ai database implementano la tolleranza d'errore.

    
risposta data 29.08.2011 - 15:23
fonte
10

Quando guardo il test del mio codice, di solito passo a una serie di processi di pensiero:

  1. Come posso suddividere questo "coso" in pezzi di dimensioni testabili? Come posso isolare solo ciò che voglio testare? Quali stub / mock dovrei creare?
  2. Per ogni blocco: come faccio a testare questo blocco per assicurarmi che risponda correttamente a un insieme ragionevole di input corretti?
  3. Per ogni blocco: come verificare che il blocco risponda correttamente agli input non corretti (puntatori NULL, valori non validi)?
  4. Come faccio a testare i limiti (ad esempio, dove i valori vanno da firmati a non firmati, da 8 bit a 16 bit, ecc.)?
  5. Quanto bene i miei test coprono il codice? Ci sono delle condizioni che ho perso? [Questo è un ottimo posto per gli strumenti di code-coverage.] Se c'è un codice che è mancato e non può mai essere eseguito, è davvero necessario essere lì? [Questa è un'altra domanda!]

Il modo più semplice che ho trovato per farlo è quello di sviluppare i miei test insieme al mio codice. Non appena ho scritto anche un frammento di codice, mi piace scrivere un test per questo. Cercare di eseguire tutti i test dopo aver codificato diverse migliaia di righe di codice con una complessità di codice ciclomatica non banale è un incubo. Aggiungere uno o due test dopo aver aggiunto poche righe di codice è davvero facile.

BTW, solo perché la società con cui lavori e / oi tuoi colleghi non eseguono il Test unitario o TDD, non significa che non puoi provarli, a meno che non siano specificamente proibiti. Forse usarli per creare codice robusto sarà un buon esempio per gli altri.

    
risposta data 29.08.2011 - 15:21
fonte
5

Oltre ai consigli forniti nelle altre risposte, ti suggerirei di utilizzare gli strumenti analisi statica (Wikipedia ha una lista di un certo numero di strumenti di analisi statici per varie lingue ) per trovare potenziali difetti prima dell'inizio dei test e per monitorare alcune metriche correlate alla testabilità del codice, come complessità ciclomatica , Riduci le misure di complessità , coesione e accoppiamento (puoi misurarle con fan-in e fan-out).

Trovare codice difficile da testare e rendere più facile il test renderà più facile scrivere casi di test. Inoltre, l'individuazione precoce dei difetti aggiungerà valore alle vostre pratiche di assicurazione della qualità (che includono i test). Da qui, acquisire familiarità con gli strumenti di test delle unità e gli strumenti di simulazione renderà più semplice l'implementazione dei test.

    
risposta data 29.08.2011 - 15:27
fonte
3

Potresti esaminare il possibile utilizzo di Tabelle della verità per aiutarti a definire tutti i potenziali percorsi del tuo codice. È impossibile tenere conto di tutte le possibilità in funzioni complesse, ma una volta stabilita la gestione per tutti i percorsi conosciuti, è possibile stabilire una gestione per il caso else.

Tuttavia, la maggior parte di questa particolare abilità è appresa dall'esperienza. Dopo aver utilizzato un determinato framework per una quantità significativa di tempo, inizi a vedere i pattern e gli earmark di comportamento che ti permetteranno di guardare un pezzo di codice e vedere dove un piccolo cambiamento potrebbe causare un errore grave. L'unico modo in cui posso pensare di aumentare la tua attitudine in questo è la pratica.

    
risposta data 29.08.2011 - 15:01
fonte
3

Se come hai detto tu non hai bisogno di test delle unità, non vedo un approccio migliore rispetto a cercare di infrangere il tuo codice manualmente.

Prova a spingi il tuo codice fino ai limiti . Ad esempio, prova a passare le variabili a una funzione che supera i limiti del limite. Hai una funzione che dovrebbe filtrare l'input dell'utente? Prova ad inserire diverse combinazioni di caratteri.

Considera il punto di vista dell'utente . Cerca di essere uno degli utenti che utilizzeranno la tua applicazione o libreria di funzioni.

    
risposta data 29.08.2011 - 15:08
fonte
3

but we don't have testers at my current employer and my colleagues seem to be pretty good at this

I tuoi colleghi devono essere davvero eccezionali per non seguire TDD o test di unità e non generare mai bug, quindi a un certo livello dubito che non stiano eseguendo alcun test di unità.

Immagino che i tuoi colleghi stiano facendo più test di quanto non si faccia, ma poiché questo fatto non è noto alla direzione, l'organizzazione ne risente di conseguenza perché la gestione ha l'impressione che il vero test non venga eseguito e i numeri dei bug sono bassi, quindi il test non è importante e il tempo non verrà programmato per questo.

Parla con i tuoi colleghi e cerca di capire che tipo di unità di test stanno facendo ed emularlo. In un secondo momento è possibile prototipare metodi migliori per il test unitario e gli attributi TDD e introdurre lentamente questi concetti nel team per un'adozione più semplice.

    
risposta data 29.08.2011 - 15:53
fonte
2
  • Scrivi i tuoi test prima di scrivere il tuo codice.
  • Ogni volta che correggi un errore che non è stato rilevato da un test, scrivi un test per rilevare il bug.

Dovresti essere in grado di ottenere una copertura su ciò che scrivi anche se la tua organizzazione non ha una copertura completa. Come tante cose in programmazione, l'esperienza di farlo ancora e ancora uno dei modi migliori per essere efficiente.

    
risposta data 29.08.2011 - 17:09
fonte
2

Oltre a tutti gli altri commenti, dal momento che dici che i tuoi colleghi sono bravi a scrivere test di percorso non felice, perché non chiedere loro di accoppiarsi con te scrivendo alcuni test.

Il modo migliore per imparare è vedere come è fatto e tirare dentro ciò che impari da quello.

    
risposta data 29.08.2011 - 18:22
fonte
2

Test della scatola nera! Dovresti creare le tue classi / metodi con i test in mente. I test dovrebbero essere basati sulle specifiche del software e dovrebbero essere chiaramente definiti sul diagramma Sequence (tramite casi d'uso).

Da quando potresti non voler fare lo sviluppo guidato dai test ...

Metti la convalida dell'input su tutti i dati in arrivo; non fidarti di nessuno Il framework .net ha generato molte eccezioni basate su argomenti non validi, riferimenti null e stati non validi. Dovresti già pensare di utilizzare la convalida dell'input sul livello dell'interfaccia utente, quindi è lo stesso trucco nel middleware.

Ma dovresti davvero fare una sorta di test automatico; quella roba salva vite.

    
risposta data 29.08.2011 - 19:53
fonte
2

Nella mia esperienza

L'unità di test, se non è completamente automatica, è inutile. È più come un capo dai capelli appuntiti che potrebbe comprare. Perché ?, perché Test Unit ti ha promesso di risparmiare tempo (e denaro) per automatizzare alcuni processi di test. Ma alcuni strumenti delle unità di test fanno il contrario, costringono i programmatori a lavorare in modo strano e costringono altri a creare test troppo estesi. La maggior parte delle volte non risparmierà l'orario di lavoro ma aumenterà il tempo di spostamento dal controllo qualità allo sviluppatore.

UML è un'altra perdita di tempo. una singola lavagna bianca + penna potrebbe fare lo stesso, più economica e veloce.

A proposito, come essere bravi a codificare (ed evitare bug)?

  • a) atomicità. Una funzione che fa un semplice (o qualche singolo compito). Perché è facile da capire, è facile da tracciare ed è facile risolverlo.

  • b) Omologia. Se, ad esempio, si chiama un database utilizzando una procedura di memorizzazione, eseguire il resto del codice.

  • c) Identifica, riduci e isola il "codice della creatività". La maggior parte del codice è praticamente copia e amp; incolla. Il codice creativo è l'opposto, un codice nuovo e che funziona da prototipo, può fallire. Questo codice è soggetto a bug logici, quindi è importante ridurlo, isolarlo e identificarlo.

  • d) Il codice "Thin ice" è il codice che si sa che è "errato" (o potenzialmente pericoloso) ma che comunque necessita, ad esempio, di codice non sicuro per un processo multi-task. Evita se puoi.

  • e) Evita il codice casella nero, questo include il codice che non è stato eseguito dall'utente (ad esempio framework) ed espressioni regolari. È facile perdere un bug con questo tipo di codice. Per esempio, ho lavorato in un progetto usando Jboss e non ho trovato un errore tranne 10 in Jboss (usando l'ultima versione stabile), era un PITA a trovarli. Evita specialmente l'ibernazione, nasconde l'implementazione, quindi i bug.

  • f) aggiungi commenti nel tuo codice.

  • g) input dell'utente come fonte di bug. identificalo. Ad esempio, SQL Injection è causato da un input dell'utente.

  • h) Identifica l'elemento non valido della squadra e separa l'attività assegnata. Alcuni programmatori sono inclini a svitare il codice.

  • i) Evita codici inutili. Se, ad esempio, la classe ha bisogno di Interfaccia , allora usala, altrimenti evita di aggiungere codice irrilevante.

a) eb) sono la chiave. Ad esempio, ho avuto un problema con un sistema, quando ho cliccato su un pulsante (salva) non ha salvato il modulo. Poi ho fatto una lista di controllo:

  • il pulsante funziona? ... sì.
  • il database memorizza qualcosa ?. no, quindi l'errore era in una fase intermedia.
  • allora, la classe che memorizza nel database funziona ?. no < - ok, ho trovato l'errore. (era correlato al permesso del database). Quindi ho controllato non solo questa procedura, ma ogni procedura che fa lo stesso (perché l'omologia del codice). Mi ci sono voluti 5 minuti per tracciare il bug e 1 minuto per risolverlo (e molti altri bug).

E un sidenote

Most of the time QA suck (as a separate section of Dev), it is useless if they are not worked in the project. They do some generic test and nothing else much. They are unable to identify most logic bugs. In my case, i was working in a prestigious bank, a programmer finished a code then send it to QA. QA approved the code and was put in production... then the code failed (an epic fail), do you know who was blamed?. yes, the programmer.

    
risposta data 30.08.2011 - 14:57
fonte
2

Un tester e un programmatore affrontano il problema da diverse angolazioni, ma entrambi i ruoli dovrebbero testare completamente la funzionalità e trovare i bug. Dove i ruoli differiscono è a fuoco. Un tester classico vede l'applicazione solo dall'esterno (ad esempio la scatola nera). Sono esperti sui requisiti funzionali dell'app. Ci si aspetta che un programmatore sia un esperto sia dei requisiti funzionali che del codice (ma tende a concentrarsi maggiormente sul codice).

(Dipende dall'organizzazione se si prevede che i programmatori siano un'esperta di requisiti. Indipendentemente da ciò, l'aspettativa implicita è lì - se progetti qualcosa di sbagliato, tu - non la persona dei requisiti - ricevi il colpa.)

Questo duplice ruolo di esperto è gravoso per la mente del programmatore e, fatta eccezione per i più esperti, può ridurre la competenza dei requisiti. Trovo che devo spostare mentalmente gli ingranaggi per prendere in considerazione gli utenti dell'applicazione. Ecco cosa mi aiuta:

  1. Debug ; imposta i breakpoint nel codice ed esegui l'applicazione. Una volta raggiunto un punto di interruzione, passa attraverso le linee mentre interagisci con l'applicazione.
  2. Test automatici ; scrivi il codice che verifica il tuo codice. Questo aiuta solo a livelli inferiori all'interfaccia utente.
  3. Conosci i tuoi tester ; possono conoscere l'applicazione meglio di te, quindi impara da loro. Chiedi loro quali sono i punti deboli della tua applicazione e quali tattiche usano per trovare i bug.
  4. Conosci i tuoi utenti ; impara a camminare nei panni dei tuoi utenti. I requisiti funzionali sono l'impronta digitale dei tuoi utenti. Ci sono spesso molte cose che i tuoi utenti conoscono sull'applicazione che potrebbero non essere chiaramente comprese nei requisiti funzionali. Comprendendo meglio i tuoi utenti, la natura del loro lavoro nel mondo reale e il modo in cui la tua applicazione dovrebbe aiutarli, capirai meglio che cosa dovrebbe essere l'applicazione.
risposta data 31.08.2011 - 14:27
fonte
2

Penso che tu voglia lavorare su due fronti. Uno è politico e consente alla tua organizzazione di adottare test a un certo livello (con la speranza che nel tempo ne adottino altri). Parla con gli ingegneri della QA al di fuori del tuo posto di lavoro. Trova elenchi di libri di QA . Dai un'occhiata a articoli pertinenti su wikipedia . Familiarizzare con i principi e le pratiche del QA. Imparare questa roba ti preparerà a rendere il caso più convincente che puoi nella tua organizzazione. Esistono buoni dipartimenti di QA e aggiungono un notevole valore alle loro organizzazioni.

Come sviluppatore individuale, adotta strategie da utilizzare nel tuo lavoro. Utilizza TDD te stesso sviluppando codice e test. Mantenere i test chiari e ben mantenuti. Se ti viene chiesto perché lo stai facendo, puoi dire che stai impedendo le regressioni e mantiene il tuo processo dei pensieri meglio organizzato (entrambi saranno vere). C'è una arte per scrivere codice testabile , impara. Sii un buon esempio per i tuoi colleghi sviluppatori.

In parte sto predicando a me stesso qui, perché faccio meno cose di quel che so che dovrei.

    
risposta data 01.09.2011 - 05:05
fonte

Leggi altre domande sui tag