Se si codifica in C, Objective-C o C ++, è possibile utilizzare l' analizzatore statico CLang per criticare la fonte senza eseguirla effettivamente.
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 debug, che fa cose come riempire di immondizia le pagine appena allocate e le pagine appena liberate, rilevare la liberazione di puntatori non allocati e scrivere alcuni dati prima e dopo ogni blocco di heap, essendo il debugger chiamato se il modello noto di tali dati cambia mai.
Qualcuno su Slashdot ha detto di aver ricavato molto valore dalla linea di sorgente sempre nuova a un singolo debugger. "Questo è tutto", ha detto. Non seguo sempre il suo consiglio, ma quando l'ho fatto mi è stato molto utile. Anche se non hai un caso di test che stimola un percorso di codice non comune, puoi modificare una variabile nel tuo debugger per prendere tali percorsi, ad esempio allocando un po 'di memoria, quindi utilizzando il debugger per impostare il tuo nuovo puntatore su NULL invece che sul indirizzo di memoria, quindi passare attraverso il gestore errori allocazione.
Usa asserzioni: la macro assert () in C, C ++ e Objective-C. Se la tua lingua non fornisce una funzione di assert, scrivine una tu.
Usa assert liberamente, quindi lasciali nel tuo codice. Chiamo assert () "Il test che continua a testare". Li uso più comunemente per verificare i presupposti nel punto di ingresso della maggior parte delle mie funzioni. Questa è una parte della "Programmazione per contratto", che è integrata nel linguaggio di programmazione Eiffel. L'altra parte sono le postcondizioni, ovvero l'uso di assert () nei punti di ritorno della funzione, ma trovo che non riesco a ottenere così tanto chilometraggio rispetto alle condizioni preliminari.
Puoi anche usare assert per controllare gli invarianti di classe. Sebbene nessuna classe sia strettamente richiesta per avere alcun invariante, le classi progettate in modo più ragionevole li hanno. Un invariante di classe è una condizione che è sempre vera diversa dall'interno delle funzioni membro che potrebbe posizionare temporaneamente l'oggetto in uno stato incoerente. Tali funzioni devono sempre ripristinare la coerenza prima di tornare.
Pertanto ogni funzione membro può controllare l'invariante all'entrata e all'uscita e la classe può definire una funzione chiamata CheckInvariant che qualsiasi altro codice può chiamare in qualsiasi momento.
Utilizza uno strumento di copertura del codice per verificare quali linee della tua sorgente vengono effettivamente testate, quindi progetta test che stimolino le linee non testate. Ad esempio, è possibile controllare i gestori di memoria insufficiente eseguendo l'app all'interno di una macchina virtuale configurata con poca memoria fisica e senza file di scambio o molto piccola.
(Per qualche motivo non sono mai stato al corrente, mentre BeOS poteva funzionare senza un file di scambio, era altamente instabile in quel modo. Dominic Giampaolo, che ha scritto il filesystem BFS, mi ha esortato a non eseguire BeOS senza scambio. vedi perché dovrebbe importare, ma deve essere stato una sorta di artefatto dell'implementazione.)
Dovresti anche testare la risposta del tuo codice agli errori I / O. Prova a archiviare tutti i tuoi file su una condivisione di rete, quindi disconnetti il cavo di rete mentre l'app ha un carico di lavoro elevato. Allo stesso modo, scollegare il cavo - o spegnere il wireless - se si sta comunicando su una rete.
Una cosa che trovo particolarmente esasperante sono i siti Web che non dispongono di un codice Javascript affidabile. Le pagine di Facebook caricano dozzine di piccoli file Javascript, ma se qualcuno di loro non riesce a scaricare, l'intera pagina si rompe. Ci deve essere solo un modo per fornire un po 'di tolleranza agli errori, ad esempio tentando un download, o per fornire un qualche tipo di fallback ragionevole quando alcuni degli script non sono stati scaricati.
Prova a 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 progettata, l'intero file verrà scritto o non verrà affatto scritto, o forse se è solo parzialmente scritto, ciò che viene scritto non verrà danneggiato, con quali dati salvati saranno completamente utilizzabili da l'app dopo aver riletto il file.
i database hanno sempre I / O su disco a tolleranza d'errore, ma quasi nessun altro tipo di app lo fa. Mentre i filesystem con journaling prevengono la corruzione del filesystem in caso di blackout o crash, non fanno nulla per prevenire il danneggiamento o la perdita dei dati dell'utente finale. Questa è la responsabilità delle applicazioni utente, ma quasi nessun altro oltre ai database implementa la tolleranza agli errori.