También he necesitado esto varias veces. He reunido una pequeña muestra a continuación, que querrás ajustar a tus necesidades. Básicamente, crea el suyo propio Appender
y lo agrega al registrador que desea. Si desea recopilar todo, el registrador raíz es un buen lugar para comenzar, pero puede usar uno más específico si lo desea. No olvide quitar el Appender cuando haya terminado, de lo contrario podría crear una pérdida de memoria. A continuación, lo hice dentro de la prueba, pero setUp
o @Before
y tearDown
o @After
podrían ser mejores lugares, según sus necesidades.
Además, la implementación a continuación recopila todo en una List
memoria. Si está registrando mucho, puede considerar agregar un filtro para descartar entradas aburridas o escribir el registro en un archivo temporal en el disco (Sugerencia: LoggingEvent
es Serializable
, por lo que debería poder serializar los objetos de evento, si su mensaje de registro es.)
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class MyTest {
@Test
public void test() {
final TestAppender appender = new TestAppender();
final Logger logger = Logger.getRootLogger();
logger.addAppender(appender);
try {
Logger.getLogger(MyTest.class).info("Test");
}
finally {
logger.removeAppender(appender);
}
final List<LoggingEvent> log = appender.getLog();
final LoggingEvent firstLogEntry = log.get(0);
assertThat(firstLogEntry.getLevel(), is(Level.INFO));
assertThat((String) firstLogEntry.getMessage(), is("Test"));
assertThat(firstLogEntry.getLoggerName(), is("MyTest"));
}
}
class TestAppender extends AppenderSkeleton {
private final List<LoggingEvent> log = new ArrayList<LoggingEvent>();
@Override
public boolean requiresLayout() {
return false;
}
@Override
protected void append(final LoggingEvent loggingEvent) {
log.add(loggingEvent);
}
@Override
public void close() {
}
public List<LoggingEvent> getLog() {
return new ArrayList<LoggingEvent>(log);
}
}
logger.getAllAppenders()
, luego pasar y llamarappender.setThreshold(Level.OFF)
a cada uno (¡y restablecerlos cuando haya terminado!). Esto asegura que los mensajes "malos" que intentas generar no aparezcan en los registros de prueba y asusten al próximo desarrollador.ListAppender<ILoggingEvent>
lugar de crear su propio accesorio personalizado.Logger
aorg.apache.logging.log4j.core.Logger
(la clase de implementación para la interfaz) tendrá accesosetAppender()/removeAppender()
nuevamente.Aquí hay una solución Logback simple y eficiente.
No requiere agregar / crear ninguna clase nueva.
Se basa en
ListAppender
: un complemento de registro de caja blanca donde las entradas de registro se agregan en unpublic List
campo que podríamos usar para hacer nuestras afirmaciones.Aquí hay un ejemplo simple.
Clase foo:
Clase de prueba:
Las aserciones JUnit no suenan muy adaptadas para afirmar algunas propiedades específicas de los elementos de la lista.
Las bibliotecas de Matcher / aserción como AssertJ o Hamcrest parecen mejores para eso:
Con AssertJ sería:
fuente
mock
la clase que está bajo prueba. Necesitas instanciarlo con elnew
operadorMuchas gracias por estas (sorprendentemente) respuestas rápidas y útiles; me pusieron en el camino correcto para mi solución.
La base de código en la que quiero usar esto, usa java.util.logging como mecanismo de registro, y no me siento lo suficientemente en casa en esos códigos para cambiar completamente eso a log4j o a las interfaces / fachadas de registro. Pero en base a estas sugerencias, `` pirateé '' una extensión de julhandler y eso funciona como un regalo.
Sigue un breve resumen. Ampliar
java.util.logging.Handler
:Obviamente, puede almacenar todo lo que quiera / desee / necesite del
LogRecord
, o empujarlos a todos en una pila hasta que obtenga un desbordamiento.En la preparación para la prueba junit, creas
java.util.logging.Logger
y le agregas una nuevaLogHandler
:La llamada a
setUseParentHandlers()
es silenciar los manejadores normales, para que (para esta ejecución de prueba junit) no ocurra un registro innecesario. Haga lo que necesite su código bajo prueba para usar este registrador, ejecute la prueba y afirme Equality:(Por supuesto, movería gran parte de este trabajo a un
@Before
método y realizaría otras mejoras variadas, pero eso saturaría esta presentación).fuente
Otra opción es simular Appender y verificar si el mensaje se registró en este appender. Ejemplo para Log4j 1.2.xy mockito:
fuente
Efectivamente, está probando un efecto secundario de una clase dependiente. Para las pruebas unitarias solo necesita verificar que
fue llamado con el parámetro correcto. Por lo tanto, use un marco de imitación para emular el registrador y eso le permitirá probar el comportamiento de su propia clase.
fuente
Aquí burlarse es una opción, aunque sería difícil, porque los registradores son generalmente estáticos privados, por lo que configurar un registrador simulado no sería pan comido, o requeriría la modificación de la clase bajo prueba.
Puede crear un Appender personalizado (o como se llame) y registrarlo, ya sea a través de un archivo de configuración solo de prueba o en tiempo de ejecución (de alguna manera, dependiendo del marco de registro). Y luego puede obtener ese apéndice (ya sea estáticamente, si se declara en el archivo de configuración, o por su referencia actual, si lo está conectando en tiempo de ejecución), y verificar su contenido.
fuente
Inspirado por la solución de @ RonaldBlaschke, se me ocurrió esto:
... que te permite hacer:
Probablemente podrías hacer que use Hamcrest de una manera más inteligente, pero lo dejé en esto.
fuente
Para log4j2, la solución es ligeramente diferente porque AppenderSkeleton ya no está disponible. Además, el uso de Mockito o una biblioteca similar para crear un Appender con un ArgumentCaptor no funcionará si espera múltiples mensajes de registro porque MutableLogEvent se reutiliza en varios mensajes de registro. La mejor solución que encontré para log4j2 es:
fuente
Como se mencionó de los demás, podría usar un marco burlón. Para que esto funcione, debe exponer el registrador en su clase (aunque preferiría que el paquete sea privado en lugar de crear un setter público).
La otra solución es crear un registrador falso a mano. Tiene que escribir el registrador falso (más código de dispositivo) pero en este caso preferiría la legibilidad mejorada de las pruebas contra el código guardado del marco de imitación.
Haría algo como esto:
fuente
Guau. No estoy seguro de por qué esto fue tan difícil. Descubrí que no podía usar ninguno de los ejemplos de código anteriores porque estaba usando log4j2 sobre slf4j. Esta es mi solución:
fuente
Esto es lo que hice para el inicio de sesión.
Creé una clase TestAppender:
Luego, en el padre de mi clase de prueba de unidad de prueba, creé un método:
Tengo un archivo logback-test.xml definido en src / test / resources y agregué un apéndice de prueba:
y agregó este apéndice al apéndice raíz:
Ahora, en mis clases de prueba que se extienden desde la clase de prueba de mi padre, puedo obtener el apéndice y obtener el último mensaje registrado y verificar el mensaje, el nivel, lo que se puede lanzar.
fuente
Para Junit 5 (Jupiter) Spring's OutputCaptureExtension es bastante útil. Está disponible desde Spring Boot 2.2 y está disponible en el artefacto spring-boot-test .
Ejemplo (tomado de javadoc):
fuente
getOut()
ogetErr()
.En cuanto a mí puede simplificar su prueba mediante el uso
JUnit
deMockito
. Propongo la siguiente solución para ello:Es por eso que tenemos una buena flexibilidad para las pruebas con diferentes cantidades de mensajes.
fuente
when(appender.isStarted()).thenReturn(true); when(appender.getName()).thenReturn("Test Appender");
convierta el registrador a "org.apache.logging.log4j.core.Logger", agregue y cambie LoggingEvent -> LogEventfuente
La API para Log4J2 es ligeramente diferente. También es posible que esté utilizando su apéndice asíncrono. Creé un apéndice enganchado para esto:
Úselo así:
fuente
Tenga en cuenta que en Log4J 2.x, la interfaz pública
org.apache.logging.log4j.Logger
no incluye los métodossetAppender()
yremoveAppender()
.Pero si no está haciendo algo demasiado elegante, debería poder lanzarlo a la clase de implementación
org.apache.logging.log4j.core.Logger
, que expone esos métodos.Aquí hay un ejemplo con Mockito y AssertJ :
fuente
Otra idea que vale la pena mencionar, aunque es un tema antiguo, es crear un productor de CDI para inyectar su registrador para que la burla sea más fácil. (Y también da la ventaja de no tener que declarar la "declaración de registrador completo", pero eso está fuera de tema)
Ejemplo:
Crear el registrador para inyectar:
El calificador:
Usando el registrador en su código de producción:
Probar el registrador en su código de prueba (dando un ejemplo de easyMock):
fuente
Usando Jmockit (1.21) pude escribir esta simple prueba. La prueba asegura que se llame un mensaje de ERROR específico solo una vez.
fuente
Burlarse del Appender puede ayudar a capturar las líneas de registro. Encuentre una muestra en: http://clearqa.blogspot.co.uk/2016/12/test-log-lines.html
fuente
Usa el siguiente código. Estoy usando el mismo código para mi prueba de integración de primavera donde estoy usando log back para iniciar sesión. Utilice el método ClaimJobIsScheduled para afirmar el texto impreso en el registro.
fuente
si está utilizando
java.util.logging.Logger
este artículo puede ser muy útil, crea un nuevo controlador y hace afirmaciones en el registro Salida: http://octodecillion.com/blog/jmockit-test-logging/fuente
Hay dos cosas que podrías estar intentando probar.
Esas dos cosas son en realidad cosas diferentes, por lo que podrían probarse por separado. Sin embargo, probar el segundo (el texto de los mensajes) es tan problemático que recomiendo no hacerlo. Una prueba del texto de un mensaje consistirá en comprobar que una cadena de texto (el texto del mensaje esperado) es la misma o puede derivarse trivialmente de la cadena de texto utilizada en su código de registro.
Tenga en cuenta que tener el código de su programa (implementando alguna lógica de negocios, tal vez) llamando directamente a la interfaz de registro de texto es un diseño deficiente (pero desafortunadamente muy común). El código responsable de la lógica empresarial también decide algunas políticas de registro y el texto de los mensajes de registro. Mezcla lógica empresarial con código de interfaz de usuario (sí, los mensajes de registro son parte de la interfaz de usuario de su programa). Esas cosas deberían estar separadas.
Por lo tanto, recomiendo que la lógica de negocios no genere directamente el texto de los mensajes de registro. En su lugar, delegue en un objeto de registro.
implements
unainterface
, que describe la API interna que puede usar su lógica empresarial.interface
.Luego, puede probar que sus clases de lógica de negocios informan correctamente a la interfaz de registro sobre los eventos, creando un registrador simulado, que implementa la API de registro interno, y utilizando la inyección de dependencia en la fase de configuración de su prueba.
Me gusta esto:
fuente
Lo que he hecho si todo lo que quiero hacer es ver que se haya registrado alguna cadena (en lugar de verificar las declaraciones de registro exactas que son demasiado frágiles) es redirigir StdOut a un búfer, hacer un contenido y luego restablecer StdOut:
fuente
java.util.logging
(aunque lo uséSystem.setErr(new PrintStream(buffer));
, porque se registra en stderr), pero no funciona (el búfer permanece vacío). si lo usoSystem.err.println("foo")
directamente, funciona, por lo que supongo que el sistema de registro mantiene su propia referencia de la secuencia de salida, que tomaSystem.err
, por lo que mi llamada aSystem.setErr(..)
no tiene ningún efecto en la salida del registro, como sucede después del inicio del sistema de registro.Respondí una pregunta similar para log4j ver cómo-puedo-probar-con-junit-that-a-warning-was-log-with-log4
Esto es más reciente y un ejemplo con Log4j2 (probado con 2.11.2) y junit 5;
Usando las siguientes dependencias de Maven
fuente
Si está utilizando log4j2, la solución de https://www.dontpanicblog.co.uk/2018/04/29/test-log4j2-with-junit/ me permitió afirmar que los mensajes se registraron.
La solución es así:
Defina un apéndice log4j como una regla ExternalResource
Defina una prueba que use su regla ExternalResource
No olvide tener log4j2.xml como parte de src / test / resources
fuente