2012-08-17 19 views
16

Es habitual ver la funcionalidad de registro en el código¿Deberíamos registrar la unidad de prueba?

public class A { 

private static final Log LOG = LogFactory.getLog(A.class); 

y el uso

 } catch (Exception e) { 
      LOG.error(e.getMessage(), e); 
      throw e; 
     } 

pero nunca vi ni siquiera una sola unidad de prueba para tal código.

Por supuesto, hago una prueba arrojando excepción y tipo de excepción, pero ¿debo escribir la prueba para verificar la información de registro? Tiendo a pensar que el registro es otra parte del comportamiento del sistema, por lo que es lógico dejarlo en las pruebas.

Suponiendo que debería cubrirlo, significa que debería cambiar el código original para inyectar el registro simulado y comprobar que se invocó el método de "error" con el mensaje esperado. ¿Pero qué hacer si mi clase original es el servicio y se crea una instancia para la primavera, debería inyectar algún registrador, así como otras dependencias?

Respuesta

9

No es su responsabilidad probar la biblioteca de registro. Pero puede valer la pena probar que cuando se lanza una excepción, su clase registra un mensaje en el nivel correcto. Lo que está probando es que su código hace lo correcto con la biblioteca de registro.

Para que el código de arriba se pueda probar, use la inyección de dependencia. Esto supone que el registrador implementa una interfaz, ILog. Pasaría el registrador como un parámetro de constructor a la clase A. Entonces, el código de prueba crearía una implementación simulada de ILog y la pasaría al constructor. No se muestra en el código anterior cómo se produce la excepción, pero presumiblemente sería a través de algún otro objeto dependiente. Así que te burlas de eso también y haces que arroje una excepción. Luego verifique que el simulacro ILog invocó el método error. Tal vez desee examinar el mensaje que registra, pero eso podría ir demasiado lejos, al hacer que el código de prueba sea frágil.

+0

es el mismo enfoque que he mencionado en mi pregunta. Pero para mí todavía no está claro qué hacer en el caso de los servicios de primavera, ya que generalmente LOG se creó para cada clase, por lo que es específico para cada clase y no puedo convertirlo en singleton. –

+0

Si no tiene ningún control sobre el código que ejemplifica el objeto de registro, es difícil probar el comportamiento, porque no puede usar un registro falso. Podrías dejar que se registre con el objeto Log real, luego verificar que se haya registrado un mensaje. Sin embargo, ¡eso es difícil de automatizar! –

+0

No, estoy de acuerdo con usted en que el código de registro debe ser probado. De lo que significa que deberíamos haber establecido para LOG, por lo que sería posible pasar pruebas simuladas de inicio de sesión, pero qué hacer en caso de servicios de primavera. Estoy pensando en 2 maneras posibles: 1) tener un setter pero llamar solo desde pruebas (podría ser difícil restringir dicha visibilidad ya que la primavera podría hacer una inyección automática), 2) inyectar el registrador durante la primavera, pero en este caso es difícil instanciar el registrador apropiado. –

2

No probaría el código de unidad que no hace más que llamar a una biblioteca en la que confía.
¿Confías en tu biblioteca de registro? Si la prueba falla, ¿es porque hay un error en la biblioteca o simplemente porque no ha configurado la biblioteca correctamente? ¿Tiene cuidado para probar la configuración?

+14

No estoy probando cómo funciona la biblioteca de registro, mi intención es probar que mi código no se registre. –

1

Sí, debemos probar el registro cuando el registro está haciendo algo que se requiere. Por ejemplo, tiene ganchos en alguna aplicación externa que escanea el registro de ciertos eventos. En ese caso, ciertamente desea asegurarse de que el registro esté hecho.

Por supuesto que no desea probar todos los eventos de registro, y creo que la mayoría de las veces solo se debe probar ERROR (y no todas).

Con los marcos de registro modernos, como SLF4j, puede simplemente insertar un controlador personalizado que almacene los eventos en la memoria y que luego puedan reafirmarse.

hay dos de ellos que vienen a la mente en este momento:

SLF4JTesting: no requiere ninguna modificación de la configuración de registro, pero requiere inyectar una fábrica de registro que podría conducir a código modificado.

SLF4J Test: No es tan potente como slf4jtesting y parece que no está desarrollado, pero funciona bien con el código existente. Sin modificaciones además de la configuración del registrador para la prueba.

Al usar SLF4J Test, las aserciones son bastante estrictas y verifican la igualdad de todos los eventos. Un matcher costumbre es probablemente interesante en tal caso:

public static Matcher<LoggingEvent> errorMessageContains(final String s) { 
    return new TypeSafeMatcher<LoggingEvent>() { 
     @Override 
     public void describeTo(final Description description) { 
      description.appendText(" type " + Level.ERROR + " should contain ") 
        .appendValue(s); 
     } 

     @Override 
     protected void describeMismatchSafely(final LoggingEvent item, final Description mismatchDescription) { 
      mismatchDescription.appendText(" was type ").appendValue(l) 
        .appendText(" message ").appendValue(item.getMessage()); 
     } 

     @Override 
     protected boolean matchesSafely(final LoggingEvent item) { 
      return item.getLevel().equals(Level.ERROR) 
        && item.getMessage().contains(s); 
     } 
    }; 
} 

Esto sólo comprueba que el mensaje contiene un texto pero no si es igual. Por lo tanto, cuando el mensaje se modifica para corregir un error tipográfico o dar más detalles, la prueba no se rompe si la parte esencial aún está contenida.

0

Si el registro es un requisito comercial, y proporcionará valor comercial (es decir, en el caso de una falla, para diagnosticar o clasificar un problema), entonces debe tratarlo como cualquier otro requisito. Como tal, probablemente debas escribir pruebas unitarias para no verificar que tu biblioteca de registro funcione, pero para verificar que, bajo las circunstancias esperadas, tu código registre lo que debería.

Más sobre este tema: https://ardalis.com/logging-and-monitoring-are-requirements

0

hay otra manera: se pueden burlarse LogFactory! por ejemplo:

import junit.framework.Assert; 
import mockit.Mock; 
import mockit.MockUp; 
import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
import org.junit.Test; 

public class XXXTest { 
    class MyLog implements Log { 
     public static final String INFO = "info"; 

     private String logLevel; 
     private Object logContent; 

     public String getLogLevel() { 
      return logLevel; 
     } 

     public Object getLogContent() { 
      return logContent; 
     } 

     @Override 
     public void info(Object o) { 
      logLevel = "info"; 
      logContent = o; 
     } 

     //Implement other methods 
    } 

    @Test 
    public void testXXXFunction() { 
     final MyLog log = new MyLog(); 
     new MockUp<LogFactory>() { 
      @Mock 
      public Log getLog(String name) { 
       return log; 
      } 
     }; 

     //invoke function and log by MyLog 
     FunctionToBeTest.invoke(); 
     Assert.assertEquals("expected log text", log.getLogContent()); 
    } 
} 

¡buena suerte!