2010-08-20 14 views
9

Il metodo seguente deve essere chiamato solo se è stato verificato che ci sono cifre non valide (chiamando un altro metodo). Come posso testare la linea throw nel seguente snippet? So che un modo potrebbe essere quello di unire insieme lo VerifyThereAreInvalidiDigits e questo metodo. Sto cercando altre idee.Come posso testare il codice che non dovrebbe mai essere eseguito?

public int FirstInvalidDigitPosition { 
    get { 
     for (int index = 0; index < this.positions.Count; ++index) { 
      if (!this.positions[index].Valid) return index; 
     } 
     throw new InvalidOperationException("Attempt to get invalid digit position whene there are no invalid digits."); 
    } 
} 

Inoltre, non vorrei scrivere un test di unità che esegua codice che non dovrebbe mai essere eseguito.

+1

Il semplice fatto che tu dica che non dovrebbe essere eseguito e non può essere raggiunto, mi dice "Rimuovi". Hai convalidato la stringa come non valida per arrivare qui, quindi sai che la proprietà restituirà qualcosa. – Michael

risposta

22

Se il "gettare" dichiarazione in questione è veramente irraggiungibile in qualsiasi scenario possibile, allora dovrebbe essere cancellati e sostituito con:

Debug.Fail("This should be unreachable; please find and fix the bug that caused this to be reached."); 

Se il codice è raggiungibile quindi scrivere uno unit test che prova questo scenario Gli scenari di segnalazione degli errori per i metodi accessibili al pubblico sono scenari perfettamente validi. Devi gestire tutti gli input correttamente, anche gli input cattivi. Se la cosa corretta da fare è lanciare un'eccezione, prova che stai generando un'eccezione.

AGGIORNAMENTO: in base ai commenti, è infatti impossibile che l'errore venga colpito e pertanto il codice non è raggiungibile. Ma ora anche Debug.Fail non è raggiungibile e non viene compilato perché il compilatore nota che un metodo che restituisce un valore ha un punto finale raggiungibile.

Il primo problema non dovrebbe essere effettivamente un problema; sicuramente lo strumento di copertura del codice dovrebbe essere configurabile per ignorare il codice di debug non raggiungibile. Ma entrambi problema può essere risolto riscrivendo il loop:

public int FirstInvalidDigitPosition 
{ 
    get 
    { 
     int index = 0; 
     while(true) 
     { 
      Debug.Assert(index < this.positions.Length, "Attempt to get invalid digit position but there are no invalid digits!"); 
      if (!this.positions[index].Valid) return index; 
      index++; 
     } 
    } 
} 

Un approccio alternativo sarebbe quello di riorganizzare il codice in modo che non si ha il problema, in primo luogo:

public int? FirstInvalidDigitPosition { 
    get { 
     for (int index = 0; index < this.positions.Count; ++index) { 
      if (!this.positions[index].Valid) return index; 
     } 
     return null; 
    } 
} 

e ora non è necessario limitare i chiamanti a chiamare prima AreThereInvalidDigits; rendi semplicemente legale chiamare questo metodo in qualsiasi momento. Sembra la cosa più sicura da fare. I metodi che esplodono quando non si effettua un controllo costoso per verificare che siano sicuri di chiamare sono metodi fragili e pericolosi.

+0

Avrei dovuto accennare, sto facendo TDD, che mi esonera dal testare casi fasulli: P E 'un metodo public class, ma module-internal. Quindi, lanciare un'eccezione non è una parte del requisito, quindi nessun test. –

+0

Anche Debug.Fail non sarà ancora copiato dal codice, sostituendo anche quel lancio con Debug.Fail è un errore di compilazione. –

+0

@ user93422: ho aggiornato la mia risposta per risolvere i vostri punti. –

4

Non capisco perché non si voglia scrivere un test unitario che esegua "codice che non dovrebbe accadere".

Se "non dovrebbe accadere", allora perché stai scrivendo il codice? Perché pensi che potrebbe accadere naturalmente - forse un errore in un futuro refactoring interromperà l'altra tua convalida e questo codice verrà eseguito.

Se pensi che valga la pena scrivere il codice, vale la pena di provarlo.

+0

Sono d'accordo: scrivere codice che "non dovrebbe mai essere eseguito" suona come un esercizio piuttosto sprecato. – EnOpenUK

+0

buon punto. Scrivo quel codice perché C# richiede il metodo non void per terminare con return o throw. Preferirei piuttosto l'eccezione di runtime in fase di esecuzione. Quindi non devo scrivere quella brutta linea. Il che significa che ho bisogno di una soluzione alternativa per la funzionalità della lingua o di trovare un difetto nel design. –

+1

Per migliorare il design, utilizzerei un metodo piuttosto che una proprietà. Una proprietà "sembra" come una semplice variabile membro accesso al chiamante, quindi è probabile che sia * sorpreso * se ci vuole molto tempo per eseguire, ha effetti collaterali (come cambiare le variabili membro in una chiamata "get"), cause eventi da licenziare o lanciare un'eccezione. D'altra parte, è normale che un metodo restituisca un valore per indicare "non trovato" (ad esempio -1, come string.IndexOf) o genera un'eccezione, quindi un metodo è una scelta di progettazione molto migliore. –

3

Parte dello scopo del test è quello di testare entrambe le cose che dovrebbero accadere e le cose che possono accadere.

Se si dispone di codice che dovrebbe mai eseguito, ma potrebbe sotto a destra (o sbagliato) le condizioni, è possibile verificare che l'eccezione si verifica nelle giuste condizioni (in Unità quadro di prova del MS) per decorare la vostra prova metodo con:

[ExpectedException(typeof(InvalidOperationException))] 
+0

Sto facendo TDD, in quanto tale non provo per cose che _can_ ma mai _should_ capita. In questo caso tale eccezione è un'indicazione di un bug nel codice e NON una condizione eccezionale. –

+0

Un modo per riaffermare ciò che hai scritto è che ci sono cose che sai possono accadere ma non stai facendo nulla! Dato che non penso che tu voglia lasciare il tuo programma aperto a comportamenti indefiniti, potresti voler pensare ancora alla tua strategia di test e codifica! Per quanto fattibile, le tue esigenze dovrebbero essere di affrontare in modo soddisfacente tutti gli input possibili, includendo quelli indesiderabili. Scrivi test per verificare che il tuo programma soddisfi questi requisiti. –

4

volte è necessario scrivere piccoli frammenti di codice che non può eseguire, solo per calmare un compilatore (ad esempio, se una funzione che gettare sempre un'eccezione, esce dall'applicazione, ecc richiamato dal una funzione che dovrebbe restituire un valore, il compilatore può insistere che il chiamante includa un "return 0", "Return Nothing" ", istruzione ecc. che non può mai essere eseguita. Non penserei che dovrebbe essere richiesto di testare tale "codice", poiché potrebbe essere impossibile eseguirlo senza danneggiare seriamente altre parti del sistema.

Una domanda più interessante viene fornita con codice che il processore non dovrebbe mai essere in grado di eseguire in circostanze normali, ma che esiste per minimizzare i danni da circostanze anomale. Ad esempio, alcune applicazioni critiche per la sicurezza che ho scritto inizio con il codice di qualcosa di simile (applicazione reale è codice macchina; Dato che segue è pseudo-codice)

 
    register = 0 
    test register for zero 
    if non-zero goto dead 
    register = register - 1 
    test register for zero 
    if non-zero goto okay1 
dead: 
    shut everything down 
    goto dead 
okay1: 
    register = register + 1 
    if non-zero goto dead 

io non sono sicuro di come esattamente si potrebbe fare per i test quel codice nel caso di errore. Lo scopo del codice è quello di garantire che se il registro ha subito un danno elettrostatico o di altro tipo o non funzioni altrimenti, il sistema si spegnerà in modo sicuro. Assenti alcune apparecchiature molto fantasiose, però, non ho idea di come testare tale codice.

Problemi correlati