Pregunta

Ocasionalmente me encuentro con una prueba de unidad que no afirma nada. El ejemplo particular que encontré esta mañana fue probar que se escribió un archivo de registro cuando se cumplió una condición. La suposición era que si no se lanzaba ningún error, la prueba fue aprobada.

Yo personalmente no tengo ningún problema con esto, sin embargo, parece ser un poco de " olor de código " para escribir una prueba de unidad que no tenga ninguna aserción asociada.

¿Solo me pregunto qué opinan las personas sobre esto?

¿Fue útil?

Solución

Esta sería la forma oficial de hacerlo:

// Act
Exception ex = Record.Exception(() => someCode());

// Assert
Assert.Null(ex);

Otros consejos

Es simplemente una prueba mínima, y ??debe documentarse como tal. Solo verifica que no explote cuando se ejecuta. La peor parte de pruebas como esta es que presentan una falsa sensación de seguridad. Su cobertura de código aumentará, pero es ilusoria. Muy mal olor.

Si no hay una afirmación, no es una prueba.

Deja de ser perezoso: puede llevarte un poco de tiempo averiguar cómo hacer que la afirmación esté ahí, pero vale la pena saber que hizo lo que esperabas que hiciera.

Estos se conocen como pruebas de humo y son comunes. Son controles básicos de sanidad. Pero no deberían ser los únicos tipos de pruebas que tienes. Aún necesitarías algún tipo de verificación en otra prueba.

Tal prueba huele. Debe verificar que el archivo se haya escrito, al menos que la hora modificada se haya actualizado quizás.

He visto bastantes pruebas escritas de esta manera que terminaron sin probar nada, es decir, el código no funcionó, pero tampoco explotó.

Si tiene algún requisito explícito de que el código bajo prueba no arroja una excepción y desea manifestar explícitamente este hecho (las pruebas como documentos de requisitos), haría algo como esto:

try
{
  unitUnderTest.DoWork()
}
catch
{
  Assert.Fail("code should never throw exceptions but failed with ...")
}

... pero esto todavía me huele un poco, probablemente porque está intentando ser negativo.

En cierto sentido, estás haciendo una afirmación implícita: que el código no produce una excepción. Por supuesto, sería más valioso tomar el archivo y encontrar la línea adecuada, pero supongo que algo es mejor que nada.

En general, veo que esto ocurre en las pruebas de integración, solo el hecho de que algo haya sido completado es suficiente. En este caso, estoy bien con eso.

Supongo que si lo viera una y otra vez en unidades de prueba me gustaría saber qué tan útiles eran las pruebas.

EDITAR: En el ejemplo dado por el OP, hay un resultado comprobable (resultado del archivo de registro), por lo que, suponiendo que si no se produjo ningún error, funcionó de forma perezosa.

Puede ser una buena solución pragmática, especialmente si la alternativa no es ninguna prueba.

El problema es que la prueba pasaría si todas las funciones llamadas fueran no-ops. Pero a veces simplemente no es posible verificar que los efectos secundarios son los que esperaba. En el mundo ideal, habría tiempo suficiente para escribir los controles para cada prueba ... pero no vivo allí.

El otro lugar donde he usado este patrón es para incrustar algunas pruebas de rendimiento con pruebas unitarias porque era una manera fácil de hacer que ejecuten cada compilación. Las pruebas no hacen valer nada, sino que miden cuánto tiempo tomó la prueba y lo registran.

He visto algo como esto antes y creo que esto se hizo solo para mantener los números de cobertura del código. Probablemente no es realmente probar el comportamiento del código. En cualquier caso, estoy de acuerdo en que (la intención) debe documentarse en la prueba para mayor claridad.

El nombre de la prueba debe documentar esto.

void TestLogDoesNotThrowException(void) {
    log("blah blah");
}

¿Cómo verifica la prueba si el registro se escribe sin aserción?

Debo admitir que nunca escribí una prueba de unidad que verificara que estaba registrando correctamente. Pero sí lo pensé y encontré este discusión de cómo se podría hacer con JUnit y Log4J. No es muy bonito pero parece que funcionaría.

Las pruebas siempre deben afirmar algo, de lo contrario, ¿qué estás probando y cómo puedes reproducir de manera consistente la evidencia de que tu código funciona?

Hacemos esto todo el tiempo. Nos burlamos de nuestras dependencias utilizando JMock, así que supongo que, en cierto sentido, el marco de JMock está haciendo la afirmación por nosotros ... pero es algo así. Tenemos un controlador que queremos probar:

Class Controller {
  private Validator validator;

  public void control(){
    validator.validate;
  }

  public setValidator(Validator validator){ this.validator = validator; }
}

Ahora, cuando probamos el controlador, no queremos probar Validator porque tiene sus propias pruebas. así que tenemos una prueba con JMock solo para asegurarnos de que llamamos validar:

public void testControlShouldCallValidate(){
  mockValidator.expects(once()).method("validate");
  controller.control;
}

Y eso es todo, no hay una " afirmación " para ver, pero cuando llama al control y la " validar " el método no se llama, entonces el marco JMock te lanza una excepción (algo así como "método esperado no invocado" o algo así).

Los tenemos por todo el lugar. Es un poco hacia atrás ya que básicamente configura su afirmación ENTONCES realiza la llamada al método probado.

A veces uso el marco de mi elección de pruebas de unidad (NUnit) para crear métodos que actúen como puntos de entrada en partes específicas de mi código. Estos métodos son útiles para perfilar el rendimiento, el consumo de memoria y el consumo de recursos de un subconjunto del código.

Estos métodos definitivamente no son pruebas unitarias (aunque están marcados con el atributo [Prueba] ) y siempre están marcados para ser ignorados y documentados explícitamente cuando se ingresan en el control de origen.

También ocasionalmente uso estos métodos como puntos de entrada para el depurador de Visual Studio. Uso Resharper para ingresar directamente a la prueba y luego al código que quiero depurar. Estos métodos no llegan al control de la fuente o adquieren sus propias afirmaciones.

Mi " real " las pruebas unitarias se construyen durante los ciclos normales de TDD y siempre afirman algo, aunque no siempre de forma directa; a veces, las afirmaciones son parte del marco de burla, y otras veces puedo refactorizar aseveraciones similares en un solo método. Los nombres de esos métodos refactorizados siempre comienzan con el prefijo " Assert " para que sea obvio para mí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top