Quanto difensivi dovremmo essere?


11

Abbiamo eseguito Pex su un po 'di codice e ha mostrato alcune cose buone (bene cose cattive, ma mostrandole prima che arrivino alla produzione!).

Tuttavia, una delle cose belle di Pex è che non smette necessariamente di cercare di trovare problemi.

Un'area che abbiamo scoperto è che quando passavamo in una stringa, non stavamo controllando le stringhe vuote.

Quindi abbiamo cambiato:

if (inputString == null)

per

if (string.IsNullOrEmpty(inputString)) // ***

Ciò ha risolto i problemi iniziali. Ma poi, quando abbiamo eseguito nuovamente Pex, ha deciso che:

inputString = "\0";

stava causando problemi. E poi

inputString = "\u0001";

Ciò che abbiamo deciso è che le impostazioni predefinite possono essere utilizzate se ci incontriamo // ***e che siamo felici di vedere l'eccezione causata da qualsiasi altro input dispari (e gestirlo).

È abbastanza?


Hai controllato per vedere se è possibile disattivare alcuni degli avvisi? So che anche JTest lo farebbe, ma era anche un'opzione per disattivare alcune delle sue raccomandazioni. Ci è voluto del tempo per modificare le impostazioni per ottenere un profilo di test del codice che ci piaceva.
FrustratedWithFormsDesigner,

@Frustrato: Sì, ci sono sicuramente delle modifiche al profilo che possiamo fare e che stiamo facendo. Pex è un ottimo strumento, comunque. I risultati mostrati hanno appena sollevato la domanda.
Peter K.

Non ho una risposta per te, ma volevo ringraziarti per il link a questa cosa Pex; sembra piuttosto pulito se è quello che penso che sia e creerà automaticamente (?) test unitari per il codice esistente. Il codice su cui sto lavorando è enorme e non ha test e codice molto stretto, quindi è impossibile refactificare qualsiasi cosa.
Wayne Molina,

@WayneM: Sì, è abbastanza buono, anche se avevo bisogno di fare qualche esempio per capire cosa stesse facendo. Per il codice legacy, è fantastico. È ancora meglio per il nuovo codice!
Peter K.

Risposte:


9

Tre domande dovrebbero aiutarti a determinare la difensiva nella tua codifica.

Primo: quali sono le conseguenze di un input errato? Se si tratta di un messaggio di errore su uno dei PC dello sviluppatore, forse non è così critico essere difensivo. Potrebbe provocare interruzioni finanziarie ai clienti, interruzione delle informazioni contabili di IE? È un sistema in tempo reale in cui le vite sono a rischio? Nello scenario vita / morte, probabilmente ci dovrebbe essere più codice di convalida e gestione errori rispetto al codice funzione reale.

In secondo luogo, quante persone riutilizzeranno questa funzione o pezzo di codice? Solo tu? Il tuo dipartimento? La tua azienda? I tuoi clienti? Maggiore è l'uso del codice, più difensivo.

Terzo: qual è la fonte dell'input che sto convalidando? Se viene inserito dall'utente o da un sito Web pubblico, sarei super difensivo. Se l'input proviene sempre dal tuo codice, sii un po 'difensivo, ma non perdere tempo a fare assegni.

Sarà sempre possibile aggiungere più controllo errori e validazione in un sistema. Il punto è che il costo di scrittura e mantenimento di questo codice supera il costo dei problemi causati da errori nel codice.


6

Gli utenti sono malvagi e tutto ciò che inseriscono dovrebbe essere verificato con il massimo rigore.

Qualunque cosa generata senza il beneficio dell'input dell'utente, o da dati pre-disinfettati, non dovrebbe essere controllata allo stesso livello. Il problema qui è quando dimentichi e usi questi metodi su dati errati, perché hai dimenticato che il codice non è stato rafforzato.

L'unica cosa che dovresti sempre controllare è tutto ciò che potrebbe causare un trabocco o un arresto anomalo. Non importa quanto profondamente sia sepolto quel metodo e quanto sei sicuro che quella condizione non possa mai verificarsi. Devi programmarlo comunque, solo per placare Murphy.


2

Sarei difensivo quanto devi esserlo. Un po 'ambiguo, credo di si ma cercherò di spiegare.

Quando hai ragione su un metodo, se quel metodo ha parametri di input devi prendere la decisione su cosa ti aspetti da quei parametri. In situazioni e luoghi all'interno di un'applicazione questa sarà diversa. Ad esempio, se un metodo o un pezzo di codice accetta dati da un input dell'utente, allora dovresti coprire tutta la base di codice e gestire qualsiasi input di conseguenza tramite un messaggio di errore o un modo carino di visualizzare dati inaccettabili.

Se il metodo è un idem API esposto. Non puoi controllare ciò che sta arrivando, quindi dovresti aspettarti di provare a coprire tutti gli aspetti e il programma di conseguenza.

Per i metodi prodotti all'interno del motore principale del tuo progetto, qui devi prendere una decisione. Suppongo che i dati in arrivo siano stati pre-selezionati e convalidati prima che arrivino o devo inserire i controlli necessari. Immagino che questo dipenda dal livello concettuale del metodo e se questo è un posto accettabile da controllare. Quindi le cose che potrei considerare sono:

1) È questo l'unico posto in cui dovrò fare questo controllo? Questa variabile dovrà essere controllata in molti posti diversi per questa condizione. In tal caso, posso fare il controllo una volta più in alto nella catena e poi assumerne la validità in seguito

2) Ci sono altri componenti del sistema che dovrebbero funzionare con i metodi e le interfacce che scrivo. In tal caso, è possibile controllare attraverso le dichiarazioni di asserzione di debug, le eccezioni di debug, i commenti sui metodi e l'architettura di sistema generale il risultato richiesto o i dati dovranno essere messi in atto.

3) Quali sono i risultati del fallimento a questo punto del codice. Provocherà il fallimento dell'intera cosa? Qualunque errore verrà rilevato altrove e tale errore verrà colto almeno.

4) Ha senso mettere un assegno qui? A volte mettere un controllo su una parte di possibili dati corrotti, anche se aiutare a risolvere il problema a quel punto e non eliminare errori potrebbe aiutare a coprirlo. A quel punto potresti passare ore a inseguire un problema diverso solo per scoprire che il problema reale era dovuto a un controllo valido sui dati che risalivano nella catena di eventi che sono ricaduti su quello segnalato dall'utente / sviluppatore.

In generale, sono un programmatore difensivo, tuttavia credo anche che con un TDD accurato e test di unità appropriati sia possibile inserire i controlli nel codice ai livelli richiesti ed essere sicuri che funzioni come dovrebbe una volta che arriva a sezioni di livello inferiore .


1

Ho trascorso settimane prima su bug che avrebbero potuto essere rilevati con 5 minuti di lavoro extra come questo in anticipo. Nella mia mente questo lavoro frontale vale sempre la pena. Alla fine risolverai il bug. L'unica domanda è quanto tempo ci vorrà.

Una cosa che questi tipi di strumenti di analisi spesso scoprono sono cose che non sono necessariamente bug, ma sono cattive abitudini di programmazione che rendono più probabili i bug. Una di queste abitudini comuni è l'inizializzazione variabile. A volte una stringa vuota è un valore perfettamente valido per una variabile. In tal caso, si desidera configurare lo strumento in modo da non considerare tale errore in quella particolare istanza. Tuttavia, spesso una stringa vuota non è un valore valido per una variabile, ma le persone lo stanno impostando comunque, perché il compilatore si lamenta se non c'è qualcosa dentro o la tua lingua si inizializza automaticamente sulla stringa vuota se è valida o meno.

Questo frustra le persone perché sembra una cattura 22 senza una soluzione valida, ma la soluzione è quella di riformattare il codice in modo che non sia necessario inizializzare la variabile fino a quando non vi è un valore valido da inserire. Ciò richiede un po 'più di riflessione, ma rende il codice molto più robusto ed è in realtà più facile da scrivere e mantenere a lungo termine.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.