2010-08-20 10 views
9

El siguiente método solo se ejecutará si se ha verificado que hay dígitos inválidos (llamando a otro método). ¿Cómo puedo probar-cubrir la línea throw en el siguiente fragmento? Sé que una forma podría ser unir el VerifyThereAreInvalidiDigits y este método. Estoy buscando otras ideas.¿Cómo puedo probar el código que nunca se debe ejecutar?

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."); 
    } 
} 

Tampoco me gustaría escribir una prueba de unidad que contenga código que nunca se debe ejecutar.

+1

El mero hecho de que diga que no debe ejecutarse y no puede ser alcanzado me dice "Quítelo". Ha validado la cadena como no válida para llegar aquí, para que sepa que la propiedad devolverá algo. – Michael

Respuesta

22

Si el "tirar" declaración en cuestión es verdaderamente inalcanzable en cualquier escenario posible, entonces debería ser suprimen y se sustituye por:

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

Si el código es accesible a continuación, escribir una prueba unitaria que prueba ese escenario. Los escenarios de informe de errores para los métodos de acceso público son escenarios perfectamente válidos. Tienes que manejar todas las entradas correctamente, incluso las malas entradas. Si lo correcto es lanzar una excepción, pruebe que está lanzando una excepción.

ACTUALIZACIÓN: de acuerdo con los comentarios, de hecho es imposible que se produzca el error y, por lo tanto, no se puede acceder al código. Pero ahora tampoco se puede acceder a Debug.Fail, y no se compila porque el compilador señala que un método que devuelve un valor tiene un punto final alcanzable.

El primer problema no debería ser realmente un problema; seguramente la herramienta de cobertura de código debería ser configurable para ignorar el código de depuración solo accesible. Pero tanto problema puede ser resuelto mediante la reescritura del bucle:

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 enfoque alternativo sería la de reorganizar el código de modo que usted no tiene el problema en el primer lugar:

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

y ahora no necesita restringir las llamadas para llamar a AreThereInvalidDigits primero; solo legalice llamar a este método en cualquier momento. Eso parece ser lo más seguro que hacer. Los métodos que explotan cuando no se hace una comprobación costosa para verificar que sean seguros para llamar son métodos frágiles y peligrosos.

+0

Debería haber mencionado, estoy haciendo TDD, lo que me exime de probar casos falsos: P Es un método público de clase, pero el método del módulo. Por lo tanto, lanzar una excepción no es parte del requisito, por lo tanto, no hay prueba. –

+0

También Debug.Fail aún no estará cubierto por código, también reemplazando ese lanzamiento con Debug.Fail es un error de compilación. –

+0

@ user93422: He actualizado mi respuesta para abordar sus puntos. –

4

No entiendo por qué no desea escribir una prueba de unidad que ejerza "código que no debería suceder".

Si "no debería suceder", entonces ¿por qué estás escribiendo el código? Como cree que podría pasar, por supuesto, quizás un error en una futura refactorización rompa su otra validación y este código se ejecutará.

Si crees que vale la pena escribir el código, vale la pena probarlo unitariamente.

+0

Estoy de acuerdo: escribir un código que "nunca debería ejecutarse" me parece un ejercicio bastante desaprovechado. – EnOpenUK

+0

buen punto. Escribo ese código porque C# requiere un método no nulo para finalizar con return o throw. Prefiero la excepción de ejecución en tiempo de ejecución en tiempo de ejecución. Así que no tengo que escribir esa línea fea. Lo que significa que necesito una solución para la función de idioma o encontrar un defecto en el diseño. –

+1

Para mejorar el diseño, usaría un método en lugar de una propiedad. Una propiedad "se ve" como una variable miembro simple acceso a la persona que llama, por lo que es probable que * se sorprenda * si lleva mucho tiempo ejecutarla, tiene efectos secundarios (como el cambio de variables miembro en una llamada 'obtener'), causas eventos para ser despedidos, o arroja una excepción. Por otro lado, es normal que un método devuelva un valor para indicar "no encontrado" (por ejemplo, -1, como string.IndexOf) o lanzar una excepción, por lo que un método es una elección de diseño mucho mejor. –

3

Parte del propósito de la prueba es para probar las dos cosas que debe pasar y cosas que pueden suceder.

Si tiene código que debe nunca se ejecutará, pero podría bajo la derecha (o mal) condiciones, se puede verificar que la excepción se produce en las condiciones adecuadas (en el marco de ensayo Unidad de EM) mediante la decoración de su prueba método con:

[ExpectedException(typeof(InvalidOperationException))] 
+0

Estoy haciendo TDD, como tal, no pruebo las cosas que _can_ pero nunca_ deberían ocurrir. En este caso, esa excepción es una indicación de un error en el código, y NO una condición excepcional. –

+0

Una forma de repetir lo que escribiste es que hay cosas que sabes que pueden suceder ¡pero no estás haciendo nada al respecto! Ya que no creo que quiera dejar su programa abierto a un comportamiento indefinido, ¡quizás quiera pensar un poco más en su estrategia de prueba y codificación! En la medida de lo posible, sus requisitos deben ser tratar satisfactoriamente todas las entradas posibles, incluidas las indeseables. Escriba pruebas para verificar que su programa cumpla con estos requisitos. –

4

a veces es necesario escribir pequeños trozos de código que no se puede ejecutar, sólo para apaciguar a un compilador (por ejemplo, si una función que siempre una excepción, sale de la aplicación, etc se llama desde una función que se supone que devuelve un valor, el compilador puede insistir en que la persona que llama incluya un "return 0", "Return Nothing" ", declaración etc. que nunca se puede ejecutar. No creo que se requiera que alguien pruebe ese "código", ya que puede ser imposible ejecutarlo sin romper seriamente otras partes del sistema.

Una pregunta más interesante viene con un código que el procesador nunca debería ser capaz de ejecutar en circunstancias normales, pero que existe para minimizar el daño causado por circunstancias anormales. Por ejemplo, algunas aplicaciones de seguridad crítica que he escrito con el código de inicio algo así como (aplicación real es el código máquina; A continuación se realiza pseudo-código)

 
    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 

No estoy seguro de cómo exactamente uno iría sobre la prueba ese código en el caso de falla. El objetivo del código es garantizar que si el registro ha sufrido daños electrostáticos o de otro tipo, o de lo contrario no funciona, el sistema se apagará de forma segura. Sin embargo, en ausencia de algunos equipos muy sofisticados, no tengo idea de cómo probar dicho código.

Cuestiones relacionadas