2009-07-22 6 views
15

Así materia de explotación de los sitios de SO y otras en Internet la mejor respuesta parece ser:Unidad de Pruebas: La tala y la inyección de dependencias

void DoSomething() { 
    Logger.Log("Doing something!"); 
    // Code... 
} 

Ahora generalmente se evitaría métodos estáticos, pero en el caso de la extracción (un caso especial) esta es la ruta más fácil y más limpia. Dentro de la clase estática, puede inyectar fácilmente una instancia a través de un archivo/marco de configuración para darle el mismo efecto que DI.

Mi problema proviene de una perspectiva de pruebas unitarias.

En el código de ejemplo anterior imagine que el punto de DoSomething() era agregar dos números juntos. Escribiría mis pruebas de unidad para esta multa. ¿Qué hay de la tala?

¿Escribiría una prueba unitaria para el registro (aunque utilizo una instancia simulada para el registrador)? Sé que si este fuera el caso, tendría que escribir una prueba de integración para demostrar que el registrador realmente escribió en un archivo de registro, pero no estoy seguro.

Siguiendo el desarrollo impulsado por prueba (lo que hago) la prueba de unidad sería necesaria para que dicte la interfaz no?

¿Algún consejo?

Respuesta

33

Personalmente, practico TDD/BDD bastante religiosamente y casi nunca pruebo el registro. Con algunas excepciones, el registro es una conveniencia del desarrollador o un factor de usabilidad, no parte de la especificación principal del método. También tiende a tener una tasa de cambio MUCHO más alta que la semántica real del método, por lo que terminas con las pruebas de interrupción solo porque agregaste un poco más de registro informativo.

Probablemente valga la pena realizar algunas pruebas que simplemente ejerciten el subsistema de registro, pero para la mayoría de las aplicaciones no probaría que cada clase utiliza el registro de una manera particular.

+0

Ese es un punto justo. Estoy muy consciente de que la cobertura del 100% del código no significa nada y, en general, esta parece ser la solución más pragmática. Como dijiste, si el registro funciona o no, no debería afectar la lógica comercial. – Finglas

+0

La cancelación del registro es exactamente la razón por la que debe probar algunas declaraciones de registro. Las entradas de registro se convierten en una fuente de señales de eventos para los sistemas de gestión aguas abajo. Debería escribir pruebas unitarias para esas fuentes, de modo que se le alertará cuando las elimine o las altere de forma que rompa el sistema descendente. Por ejemplo, si las operaciones extraen sus registros para fallas de compromiso, debe asegurarse de que el contenido de esos mensajes se conserve incluso cuando cambie el comportamiento o agregue o disminuya la fidelidad de registro de otras maneras. Una gran relación con las operaciones es clave para recolectar casos de uso real para que se conviertan en pruebas. –

12

Solo he escrito algunas pruebas unitarias para el registro. Es un problema, ya sea desordenar el código de producción (debido a la inyección del registrador) o el olor a prueba (reemplazar el registrador estático con un simulacro). A diferencia de McWafflestix, a menudo no he encontrado que valga la pena el esfuerzo después.

¿Cuánto necesita realmente para saber si el registro funciona, más allá de lo que verá a través de otras pruebas (prácticas)? Es posible que desee utilizar DI para la clase ocasional donde el registro es realmente importante, pero de lo contrario no me molestaría en probar el registro.

Esto supone que el registro es de naturaleza de depuración; si se trata de un registro de auditoría o algo así (algo con un requisito funcional), esa es una cuestión diferente.

+1

Bueno, me han dicho que nunca escribes una línea de código sin escribir una prueba para demostrar su valía (TDD), así que esa es la motivación detrás de esto. – Finglas

+2

@Dockers, probablemente citado hasta la muerte, pero aquí está el enfoque de Kent Beck. Creo que esa línea es más para principiantes. Una vez que sepa lo que está haciendo, puede evitar las cosas, como sugiere Jon. http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests/153565#153565 – Yishai

+0

Excelente. No tenía idea de que estaba aquí, lea el mensaje de Kent Becks. Veré toda la discusión. Aclamaciones. – Finglas

0

Yo diría que probablemente sea una cosa razonable escribir una Prueba Unitaria para el registro; Lo he hecho antes, y resultó ser más útil de lo que inicialmente había previsto. En general, el registro de pruebas de unidades le da una buena garantía de que la instalación de registro funciona en el tiempo de prueba de la unidad, lo que puede ser una buena garantía. No creo que sea crítico para el registro de pruebas unitarias, pero si tienes la inclinación, dudo que sea algo malo tampoco.

0

Personalmente, creo que es excesivo para las declaraciones de registro de prueba de la unidad. Pero si realmente quiere hacerlo, entonces funcionará un registrador simulado o, si usa un marco como log4j, escriba un appender personalizado que se use durante las pruebas.

4

La mayoría de los marcos de registro le permiten proporcionar una implementación personalizada para los componentes. Puede usar ese mecanismo de configuración para proporcionar sus propias implementaciones.


Por ejemplo, Log4J de Java le permite declarar encargo appenders, que son los componentes responsables de 'entregar' un LoggingEvent.

Un registrador puede ser fácilmente burlado y se inyecta usando:

Appender appenderMock = EasyMock.createMock(Appender.class); 
/* expect */ appenderMock.doAppend(EasyMock.isA(LoggingEvent.class)); 
EasyMock.replay(appenderMock); 

Logger.getLogger(/* replace with your own */"My logger").addAppender(appenderMock); 

EasyMock.verify(appenderMock); 

Esta prueba sólo se verifica que un registro de eventos se envía, pero se puede afinar mucho más usando EasyMock.

0

Por lo general, no compruebo las declaraciones de inicio de sesión afirmando lo que está registrado, pero compruebo que las rutas de código tomadas por las pruebas de mi unidad cubren las declaraciones de registro solo para asegurarme de no obtener una excepción al registrar una excepción.

9

Yo dividiría el registro en tres categorías:

1) Un requisito. Algunos sistemas requieren el registro para fines de auditoría, o para llenar algún otro requisito del proyecto (como un estándar de registro en un servidor de aplicaciones). Entonces, de hecho es un requisito y merece pruebas unitarias y pruebas de aceptación hasta el punto en que pueda estar seguro de que se cumple el requisito. Entonces, en este caso, se puede probar la cadena exacta del registro.

2) Solución de problemas. En caso de que empiece a tener un estado extraño en QA o producción, quiere poder rastrear lo que está sucediendo. En general, diría que si esto es importante (por ejemplo, en una aplicación con muchos subprocesos donde el estado puede complicarse pero no puede reproducirse a través de pasos conocidos), entonces puede ser valioso probar que los valores de estado dados terminen registrados (por lo que no está 't probar toda la legibilidad del registro, solo que ciertos hechos entran ahí). Incluso si la clase se cambia posteriormente, es probable que ese estado se registre (junto con el estado adicional) por lo que el acoplamiento entre la prueba y el registro es razonable. Entonces, en este caso, solo se prueban partes del registro (una prueba contiene).

3) Una ayuda al desarrollo. En muchos casos utilizo el registro como una forma más sólida de comentar. Se puede escribir una declaración como:

logger.debug("Extract the fifth instance of BLAH from the string " + s); 

Para que pueda documentar el código y al mismo tiempo tener un artefacto útil si alguna vez tiene que depurar lo que está pasando. En ese caso, no probaría la unidad en absoluto, ya que la existencia o no de una declaración dada no es importante por sí misma.

En cuanto a la vista de que debe probar el 100% de todo, consulte la respuesta de Kent Beck here. Creo que la "prueba de todo" es un buen consejo para los principiantes, porque cuando comiences con TDD, la tentación será no probar nada que sea difícil de probar, o que te empuje a pensar en el diseño para hacerlo comprobable, y racionalizarlo como poco importante. Pero una vez que sabes lo que estás haciendo y aprecias el valor de las pruebas, entonces es importante equilibrar lo que estás haciendo con lo que vale la pena probar.

+1

+1 para dividir el registro entre el requisito comercial y el requisito técnico. –

0

Probablemente tendría un cuerpo separado de pruebas de unidad para el registrador en sí, para probar sus diversas funciones por separado de todo lo demás. En los métodos que usan el registrador, solo probaría que el registrador se invocó (es decir, se esperaba que se llamara) con los parámetros correctos.Por ejemplo, si tengo un método:

DoSomething() 
{ 
    if (FatalErrorOccurred) 
    { 
     Logger.Log("Fatal Error", ErrorLevel.Fatal); 
    } 
} 

que iba a escribir una prueba que muestra el registrador registra un mensaje de error grave cuando FatalErrorOccurred era cierto. Por supuesto, no probaría el contenido del mensaje de error, ya que es muy susceptible de cambios.

0

Aunque estoy de acuerdo con otros en que no aplicaría TDD al registro, trataría de garantizar que las pruebas unitarias cubran todas las rutas de código que contienen instrucciones de registro. Y, lo que es más importante, asegúrese de que el nivel más alto de verbosidad se configure al ejecutar las pruebas unitarias, para que se ejecuten todas las instrucciones de registro.

Por ejemplo, el siguiente código tiene un error que arrojará una excepción FormatException solo si el seguimiento de nivel de depuración está habilitado.

if (logger.IsDebugEnabled) 
{ 
    string message = String.Format(CultureInfo.CurrentCulture, 
      "... {1} ...", someValue); 
    logger.Debug(message); 
} 
Cuestiones relacionadas