Prueba JUnit para System.out.println()
Necesito escribir pruebas JUnit para una aplicación antigua que está mal diseñada y escribe muchos mensajes de error en la salida estándar. Cuando el getResponse(String request)
método se comporta correctamente, devuelve una respuesta XML:
@BeforeClass
public static void setUpClass() throws Exception {
Properties queries = loadPropertiesFile("requests.properties");
Properties responses = loadPropertiesFile("responses.properties");
instance = new ResponseGenerator(queries, responses);
}
@Test
public void testGetResponse() {
String request = "<some>request</some>";
String expResult = "<some>response</some>";
String result = instance.getResponse(request);
assertEquals(expResult, result);
}
Pero cuando obtiene un formato XML incorrecto o no comprende la solicitud, regresa null
y escribe algunas cosas en la salida estándar.
¿Hay alguna forma de hacer valer la salida de la consola en JUnit? Para detectar casos como:
System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
usar ByteArrayOutputStream y System.setXXX es simple:
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;
@Before
public void setUpStreams() {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}
@After
public void restoreStreams() {
System.setOut(originalOut);
System.setErr(originalErr);
}
casos de prueba de muestra:
@Test
public void out() {
System.out.print("hello");
assertEquals("hello", outContent.toString());
}
@Test
public void err() {
System.err.print("hello again");
assertEquals("hello again", errContent.toString());
}
Utilicé este código para probar la opción de línea de comando (afirmando que -version genera la cadena de versión, etc., etc.)
Editar:
versiones anteriores de esta respuesta llamadas System.setOut(null)
después de las pruebas; Esta es la causa de las NullPointerExceptions a las que se refieren los comentaristas.
Sé que este es un hilo antiguo, pero hay una buena biblioteca para hacer esto: Ejemplo de reglas del sistema
de los documentos:
public void MyTest {
@Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
@Test
public void overrideProperty() {
System.out.print("hello world");
assertEquals("hello world", systemOutRule.getLog());
}
}
También le permitirá capturar System.exit(-1)
y otras cosas para las que sería necesario probar una herramienta de línea de comandos.
En lugar de redirigir System.out
, refactorizaría la clase que usa System.out.println()
pasando a PrintStream
como colaborador y luego usándola System.out
en producción y Test Spy en la prueba. Es decir, utilice la inyección de dependencia para eliminar el uso directo del flujo de salida estándar.
En producción
ConsoleWriter writer = new ConsoleWriter(System.out));
En la prueba
ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));
Discusión
De esta manera, la clase bajo prueba se vuelve comprobable mediante una simple refactorización, sin necesidad de una redirección indirecta de la salida estándar o una intercepción oscura con una regla del sistema.
Puede configurar el flujo de impresión System.out a través de setOut() (y para in
y err
). ¿Puedes redirigir esto a una secuencia de impresión que graba en una cadena y luego inspeccionarla? Éste parecería ser el mecanismo más sencillo.
(Yo recomendaría, en algún momento, convertir la aplicación a algún marco de registro, ¡pero sospecho que ya lo sabe!)
Ligeramente fuera de tema, pero en caso de que algunas personas (como yo, cuando encontré este hilo por primera vez) puedan estar interesadas en capturar la salida del registro a través de SLF4J, el JUnit de commons-testing@Rule
podría ayudar:
public class FooTest {
@Rule
public final ExpectedLogs logs = new ExpectedLogs() {{
captureFor(Foo.class, LogLevel.WARN);
}};
@Test
public void barShouldLogWarning() {
assertThat(logs.isEmpty(), is(true)); // Nothing captured yet.
// Logic using the class you are capturing logs for:
Foo foo = new Foo();
assertThat(foo.bar(), is(not(nullValue())));
// Assert content of the captured logs:
assertThat(logs.isEmpty(), is(false));
assertThat(logs.contains("Your warning message here"), is(true));
}
}
Descargo de responsabilidad :
- Desarrollé esta biblioteca porque no pude encontrar ninguna solución adecuada para mis propias necesidades.
- Por el momento sólo están disponibles enlaces para
log4j
,log4j2
ylogback
, pero estaré feliz de agregar más.