2009-07-13 39 views
265

Necesito escribir pruebas JUnit para una aplicación antigua que está mal diseñada y está escribiendo una gran cantidad de mensajes de error a la salida estándar. Cuando el método getResponse(String request) comporta correctamente devuelve una respuesta XML:Prueba JUnit para System.out.println()

@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 se pone XML mal formado o no entiende la petición devuelve null y escribe algunas cosas a la salida estándar.

¿Hay alguna manera de afirmar la salida de la consola en JUnit? Para la captura de casos como:

System.out.println("match found: " + strExpr); 
System.out.println("xml not well formed: " + e.getMessage()); 
+0

relacionada con, pero no es un duplicado de http://stackoverflow.com/questions/3381801/how-do-i-unit-test-saving-file-to-the-disk – Raedwald

Respuesta

445

usando ByteArrayOutputStream y System.setXXX es simple:

private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); 

@Before 
public void setUpStreams() { 
    System.setOut(new PrintStream(outContent)); 
    System.setErr(new PrintStream(errContent)); 
} 

@After 
public void restoreStreams() { 
    System.setOut(System.out); 
    System.setErr(System.err); 
} 

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()); 
} 

que utilizan este código para probar la opción de línea de comandos (afirmando que -versión emite la cadena de versión, etc, etc)

Editar: Las versiones anteriores de esta respuesta llamados System.setOut(null) después de las pruebas; Esta es la causa de que los comentaristas NullPointerExceptions se refieran a.

+0

Además, he usado JUnitMatchers para probar las respuestas: assertThat (result, containsString ("

+3

Prefiero usar System.setOut (null) para restaurar la secuencia a la que era cuando se inició la VM – tddmonkey

+5

Los javadocs no dicen nada acerca de poder pasar null a System.setOut o System.setErr. ¿Estás seguro de que esto funcionará en todos los JRE? – finnw

19

Es posible ajustar el flujo de impresión a través de System.out setOut() (y para in y err). ¿Puedes redirigir esto a una secuencia de impresión que graba en una cadena y luego inspeccionar eso? Ese parece ser el mecanismo más simple.

(! Abogaría, en algún momento, convertir la aplicación en cierta marco de registro - pero sospecho que ya son conscientes de ello)

+1

Eso fue algo que vino en mi opinión, pero no podía creer que no haya una forma estándar de JUnit para hacer eso. Gracias, cerebro. Pero los créditos llegaron a dfa por el esfuerzo real. –

7

@dfa answer es genial, así que di un paso más para poder probar los bloques de salida.

Primero creé TestHelper con un método captureOutput que acepte la clase annoymous CaptureTest. El método captureOutput hace el trabajo de configurar y destruir las secuencias de salida. Cuando se llama a la implementación del método CaptureOutputtest, tiene acceso a la salida generada para el bloque de prueba.

Fuente de TestHelper:

public class TestHelper { 

    public static void captureOutput(CaptureTest test) throws Exception { 
     ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 
     ByteArrayOutputStream errContent = new ByteArrayOutputStream(); 

     System.setOut(new PrintStream(outContent)); 
     System.setErr(new PrintStream(errContent)); 

     test.test(outContent, errContent); 

     System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); 
     System.setErr(new PrintStream(new FileOutputStream(FileDescriptor.out))); 

    } 
} 

abstract class CaptureTest { 
    public abstract void test(ByteArrayOutputStream outContent, ByteArrayOutputStream errContent) throws Exception; 
} 

Tenga en cuenta que TestHelper y CaptureTest se definen en el mismo archivo.

Luego en su prueba, puede importar la captura estática de salida. Aquí está un ejemplo utilizando JUnit:

// imports for junit 
import static package.to.TestHelper.*; 

public class SimpleTest { 

    @Test 
    public void testOutput() throws Exception { 

     captureOutput(new CaptureTest() { 
      @Override 
      public void test(ByteArrayOutputStream outContent, ByteArrayOutputStream errContent) throws Exception { 

       // code that writes to System.out 

       assertEquals("the expected output\n", outContent.toString()); 
      } 
     }); 
} 
2

Usted no quiere volver a dirigir la corriente system.out ya que vuelve a dirigir para la JVM TODO. Cualquier otra cosa que se ejecute en la JVM puede desordenarse. Hay mejores formas de probar la entrada/salida. Mira en trozos/burlas.

79

Sé que esto es un viejo hilo, pero hay una buena biblioteca para hacer esto:

System Rules

Ejemplo de la documentación:

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á a la trampa System.exit(-1) y otras cosas que una herramienta de línea de comandos necesitaría ser probada.

15

En lugar de redirigir System.out, me refactorizar la clase que utiliza System.out.println() pasando un PrintStream como colaborador y luego usando System.out en la producción y una prueba Spyen la prueba.

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 convierte comprobable mediante una simple refactorización, sin tener la necesidad de indirectos redirección de la salida estándar o intercepción oscura con una regla de sistema.

+1

No pude encontrar este ConsoleWriter en ningún lugar del JDK: ¿dónde está? –

+2

Probablemente debería mencionarse en la respuesta, pero creo que la clase fue creada por user1909402. – Sebastian

+3

Creo que 'ConsoleWriter' es el sujeto de prueba, –

10

poco fuera de tema, pero en el caso de algunas personas (como yo, cuando me encontré por primera vez este hilo) podrían estar interesados ​​en la captura de la salida del registro a través de SLF4J, commons-testing 's JUnit @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 necesidades.
  • Solo están disponibles los enlaces para log4j, log4j2 y logback, pero me complace agregar más.
+0

¡Muchas gracias por crear esta biblioteca! ¡He estado buscando algo como esto por tanto tiempo! Es muy, muy útil ya que a veces simplemente no puedes simplificar tu código lo suficiente como para ser fácilmente comprobable, ¡pero con un mensaje de registro puedes hacer maravillas! – carlspring

+0

Esto parece realmente prometedor ... pero incluso cuando solo copie su programa ATMTest y lo ejecute como prueba en Gradle, recibo una excepción ... He planteado un problema en su página de Github ... –

1

No se puede imprimir directamente usando System.out.println o el uso de registrador API durante el uso de JUnit. Pero si usted quiere comprobar todos los valores a continuación, sólo tiene que utilizar

Assert.assertEquals("value", str); 

arrojará a continuación error de aserción:

java.lang.AssertionError: expected [21.92] but found [value] 

Su valor debe ser 21.92, Ahora si va a probar usando este valor como el de abajo su caso de prueba pasará

Assert.assertEquals(21.92, str); 
5

Si estaba utilizando la primavera de arranque (usted mencionó que se trabaja con una aplicación antigua, por lo que es probable que no está pero podría ser de utilidad para otros), entonces se podría utilizar org.springframework .boot.test.rule.OutputCapture de la siguiente manera:

@Rule 
public OutputCapture outputCapture = new OutputCapture(); 

@Test 
public void out() { 
    System.out.print("hello"); 
    assertEquals(outputCapture.toString(), "hello"); 
} 
+1

I votó su respuesta porque uso Spring boot y me puso en el camino correcto. ¡Gracias! Sin embargo, outputCapture debe inicializarse. (public OutputCapture outputCapture = new OutputCapture();) Consulte https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-output-capture- utilidad de prueba – EricGreg

+0

Tiene toda la razón. ¡Gracias por el comentario! He actualizado mi respuesta. – Disper

0

para salir

@Test 
void it_prints_out() { 

    PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out)); 

    System.out.println("Hello World!"); 
    assertEquals("Hello World!\r\n", out.toString()); 

    System.setOut(save_out); 
} 

para err

@Test 
void it_prints_err() { 

    PrintStream save_err=System.err;final ByteArrayOutputStream err= new ByteArrayOutputStream();System.setErr(new PrintStream(err)); 

    System.err.println("Hello World!"); 
    assertEquals("Hello World!\r\n", err.toString()); 

    System.setErr(save_err); 
} 
Cuestiones relacionadas