2012-02-10 9 views
5

He estado tratando de resolver este problema: Cómo crear una prueba unitaria que pruebe si una función aborta porque falla un Debug.Assert (de System.Diagnostics), marcándolo como pasó cuando hace esto y como fallido si no lo hace.Cómo garantizar una depuración.Asegure los incendios correctamente usando NUnit

NUnit tiene la característica [ExpectedException(typeof(ArgumentException))], pero parece que no puedo encontrar qué tipo de excepción es del sitio web de MSDN. La intuición diría que podría ser algo así como una AssertionException, y esa existe ... pero es parte del marco NUnit. Supongo que esa es la excepción de NUnit assert throw around. Puede ser que sea capaz de simplemente nuke mediante el uso de:

[ExpectedException(typeof(Exception))] 

Pero esto da lugar al problema de que las ventanas estándar ventana de depuración muestra. En mi búsqueda, encontré formas de eliminar esta ventana para que no apareciera, pero parece como llevar una cuchilla de carnicero a la mesa de cirugía donde normalmente usas un bisturí. Porque quiero poder ver esta ventana cuando sucede algo inesperado, cuando ejecuto mi programa.

supongo que es la solución de la sustitución del método Debug.Assert con el NUnit contraparte (todavía estoy al principio de mi proyecto, por lo que no es una demasiado grande de una refactorización), pero supongo que muchos programadores se pega con Debug.Assert funcionalidad como es estándar en .NET.

Como tal, me gustaría saber cómo 'afirmar' Debug.Assertion fallas, sin tener que 'asesinar' la pantalla de depuración de Windows de mi proyecto?

Para tener un ejemplo concreto de un contrato en mi código, hay un ejemplo a continuación. Para aquellos que pueda parecer familiar, es la tabla To-Wound del Warhammer 40K tabletop wargame escrito como una función.

static public int assaultToHit(int _attacker_WeaponSkill, 
      int _defender_WeaponSkill) 
     { 
      //Preconditions 
      Debug.Assert(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10, 
       "Weapon Skill stat must be in range [1,10]"); 
      Debug.Assert(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10, 
       "Weapon Skill stat must be in range [1,10]"); 

      int target; 
      if (_attacker_WeaponSkill > _defender_WeaponSkill) 
      { 
       target=3; 
      } 
      else if (_defender_WeaponSkill >= (_attacker_WeaponSkill + _attacker_WeaponSkill + 1)) 
      { 
       target=5; 
      } 
      else 
      { 
       target=4; 
      } 

      //postconditions 
      Debug.Assert(target >= 3 && target <= 5, 
       "To hit target for assault must be in range [3,5]"); 

      return target; 
     } 

La función de poner a prueba las condiciones previas estaría en la línea de algo como esto:

[TestCase(-1,2)] 
    [TestCase(1, -2)] 
    [TestCase(-1, -2)] 
    [TestCase(11, 2)] 
    [TestCase(1, 20)] 
    [TestCase(11, 20)] 
    [ExpectedException(typeof(Exception))] 
    public void testContract_AssaultToHit(int _attacker_weaponskill, 
     int _defender_weaponskill) 
    { 
     Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
      _defender_weaponskill); 
    } 
+1

Eso no es una solución, es lo que deberías estar haciendo. Debug.Asert solo se activa al compilar con depuración activada. Es una herramienta de investigación opcional. Absolutamente nada en su código liberado debe confiar en él para un funcionamiento normal, por lo que no tiene sentido realizar una prueba unitaria de ello. –

+0

@TonyHopkinson Tienes un punto ahí. Pero en la misma medida, uno podría usar Trace.Assert en lugar de usar NUnit Asserts. En ese caso, no necesitaría agregar NUnit framework .dll junto con el resto de su código, ¿verdad? En ese caso, la pregunta seguiría siendo la misma, creo: ¿cómo se puede probar que el Trace.Assert/Debug.Asertó el disparo correctamente usando NUnit? – Xilconic

+0

Nunca pensé en eso para ser honesto. Un Assert es un Assert, no trataría de probarlo más de lo que lo haría con writelines, alertas y MessageBoxes mientras trataba de descubrir por qué mi código no estaba haciendo lo que pensé que debería hacer. Una especie de quien mira la pregunta de los perros guardianes, ¿no es así? Interesante pregunta, pero no veo mucho uso para la respuesta ... –

Respuesta

2

De https://stackoverflow.com/a/117247/605538 uno puede encontrar una recomendación para usar excepciones para la interfaz pública, durante el uso de afirmaciones para verificar el código interno. Cuando se prueban las condiciones previas de una función (y se puede aplicar un pensamiento similar para las condiciones posteriores, aunque yo personalmente preferiría usar afirmaciones allí), se puede recomendar el uso de excepciones en lugar de usar aserciones.

el uso de excepciones en lugar de resultarían en una muestra de la siguiente manera:

static public int assaultToHit(int _attacker_WeaponSkill, 
     int _defender_WeaponSkill) 
    { 
     //Preconditions 
     if(!(_attacker_WeaponSkill >= 1 && _attacker_WeaponSkill <= 10)) 
     { 
      throw new ArgumentOutOfRangeException("Attackers WeaponSkill must be in range [1,10]"); 
     } 
     if(!(_defender_WeaponSkill >= 1 && _defender_WeaponSkill <= 10)) 
     { 
      throw new ArgumentOutOfRangeException("Defenders WeaponSkill must be in range [1,10]"); 
     } 

     ... 
     //rest unchanged 
    } 

Junto con la siguiente prueba NUnit:

[TestCase(-1,2)] 
[TestCase(1, -2)] 
[TestCase(-1, -2)] 
[TestCase(11, 2)] 
[TestCase(1, 20)] 
[TestCase(11, 20)] 
[ExpectedException(typeof(ArgumentOutOfRangeException))] 
public void testContract_AssaultToHit(int _attacker_weaponskill, 
    int _defender_weaponskill) 
{ 
    Warhammer40kRules.assaultToHit(_attacker_weaponskill, 
     _defender_weaponskill); 
} 

barra lateral [12 de junio de 2014]: tengo Recientemente he llegado a la conclusión de que no debería tener que probar las infracciones de precondición en el contexto de Diseño por contrato. Si diseña con DbC en mente, básicamente declara lo siguiente: "Si llama a un método y cumple con sus condiciones previas, se le garantiza un cierto comportamiento. Si no cumple con las condiciones previas del método, puede esperar un comportamiento indefinido". En el sentido más amplio del término "comportamiento indefinido", esto significa que no puede esperar ningún tipo de estado. Es posible que obtenga excepciones, tal vez nada sucede en absoluto y tal vez su disco duro se formatea (bueno ... entiendes el punto;)). Como tal, no puedes probar para 'comportamiento indefinido'.

Lo que puede probar, es su programación defensiva que asegura que un fallo de precondición no haga que la función funcione, por ejemplo lanzando un Exception. Este comportamiento es comprobable.

5

Parece que haya utilizado instalaciones equivocadas para controlar un flujo de aplicación en caso de error. Debug.Assert() no se debe utilizar para conducir un flujo de lógica de aplicación.

La Prueba de unidad debe cubrir un caso real de prueba y parece que o bien está tratando de implementar una prueba incorrecta o necesita lanzar una excepción/etc en lugar de usar Debug.Assert(). Puede compartir un código para que sea posible darle algunos consejos específicos.

De todos modos, puede leer MSDN sobre cómo agregar un escucha de seguimiento personalizado e interceptar llamadas Assert.

Enlaces de interés:

+0

He proporcionado código de muestra en mi publicación.Actualmente estoy usando Debug.Assert para verificar mi contrato de código, según la metodología Design by Contract. Me gustaría escribir pruebas de NUnit para probar/verificar mi contrato, que de hecho mi contrato se activa cuando no lo estoy cumpliendo. Estoy usando aserciones en lugar de excepciones aquí para mantener el código más fácil de leer y aun así lograr el mismo objetivo. La creación de un Oyente de seguimiento personalizado todavía se siente como 'asesinar' a toda la ventana "Anular, volver a intentar, ignorar". No quiero redirigir _all_ assertion, tal vez solo las que he creado para pruebas unitarias. – Xilconic

+0

@Xilconic: Creo que deberás lanzar 'ArgumentNullExcetion'rather que usign' Debug.Assert() 'para el error de validación de argumento – sll

+0

Sí, he estado viendo cuál es el consenso general acerca de cuándo usar un assert y cuándo lanzar un excepción. En http://stackoverflow.com/a/117247/605538 se recomienda utilizar excepciones para funciones públicas, y afirma para asuntos 'privados'. De hecho, estoy de acuerdo con su razonamiento, así que sigo tu sugerencia y la de Dima, y ​​cambio de una afirmación a una excepción. – Xilconic

1

Para su caso particular, le recomendaría echar un vistazo a Microsoft Code Contracts, ya que el caso de afirmación específico que tiene se dirige a verificar los contratos de entrada para sus funciones.

Si Code Contracts es demasiado grande para usted, le recomiendo que haga que sus contratos generen excepciones cuando no se cumplen los contratos implícitos, en lugar de afirmar.

Sin embargo, los archivos de depuración siguen siendo valiosos, pero deberían utilizarse en situaciones que tienen menos posibilidades de suceder, como después de una declaración que hace que una llamada devuelva nulo en lugar de la colección vacía que esperaba.

+0

He estado trabajando con Code Contracts desde hace un tiempo, y estoy de acuerdo con usted. Llegué a la conclusión de que el uso de Contratos de código para la verificación previa de condiciones es superior a los patrones if-then-exception. Pero, por supuesto, esto es solo una opción si estás con C# como lo estoy haciendo ahora (o tienes un lenguaje de programación con soporte integrado para Design by Contract). De lo contrario, usar el patrón if-then-exception sigue siendo el camino a seguir. – Xilconic

Cuestiones relacionadas