Necesito escribir las pruebas JUnit para una aplicación antigua que está mal diseñado y está escribiendo un montón de mensajes de error a la salida estándar. Cuando el getResponse(String request) método se comporta correctamente devuelve un XML de respuesta:

@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 llega XML con formato incorrecto o no entiende la petición devuelve null y escribe algunas cosas en la salida estándar.

¿Hay alguna forma de hacer valer 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());
InformationsquelleAutor Mike Minicki | 2009-07-13

12 Comentarios

  1. 543

    utilizando ByteArrayOutputStream y del Sistema.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 ejemplo:

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

    He utilizado este código para la prueba de la opción de línea de comandos (la afirmación de que la versión de salidas de la cadena de versión, etc etc)

    Edición:
    Las versiones anteriores de esta respuesta se llama System.setOut(null) después de las pruebas; Esta es la causa de NullPointerExceptions comentaristas de referencia.

    • Ademã ¡s, he utilizado JUnitMatchers para la prueba de respuestas: assertThat(resultado, containsString(«<solicitud:GetEmployeeByKeyResponse»)); Gracias, dfa.
    • Yo prefiero el uso del Sistema.setOut(null) para restaurar el flujo de volver a lo que era cuando la VM fue lanzado
    • Los javadocs no dicen nada acerca de ser capaz de pasar el valor null para el Sistema.pantalla o Sistema.setErr. Está usted seguro de que esto funcionará en todos los JREs?
    • Me encontré con un NullPointerException en otras pruebas después de establecer una nulo flujo de error como se sugirió anteriormente (en java.io.writer(Object), llamado internamente por un validador de XML). Yo sugeriría que en lugar de guardar el original en un campo: oldStdErr = System.err y restauración de este en el @After método.
    • Estoy teniendo un problema con EclEmma con esta solución. JUnit4 está trabajando perfectamente bien. Pero cada vez estoy llamando outContent/errContent en una prueba, entonces la cobertura de código está mal realizado y la rama está marcado como «inexplorado» a pesar de que, obviamente, ejecutar a través de él durante la prueba. Debo añadir que yo también estoy con el Sistema de Reglas de la biblioteca. Si alguien ya ha tenido este problema y tengo una solución que yo estaría feliz de saber más sobre él.
    • Gran solución. Sólo una nota para cualquier persona que la utilice, puede que tenga que recortar() en blanco/caracter de outContent.
    • Este enfoque está plagado de problemas, debido a que el flujo de salida estándar es un recurso compartido utilizado por todas las partes de su programa. Es mejor usar la Inyección de dependencias para eliminar el uso directo de la secuencia de salida estándar: stackoverflow.com/a/21216342/545127
    • en el momento en el que parece ser el signle opción para conseguir que funcione con Junit 5 (no tiene @Reglas más)
    • @BeforeAll y @AfterAll para JUnit5.

  2. 99

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

    Las Reglas Del Sistema

    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 usted para atrapar System.exit(-1) y otras cosas que una herramienta de línea de comando tendría que ser probado para.

    • Este enfoque está plagado de problemas, debido a que el flujo de salida estándar es un recurso compartido utilizado por todas las partes de su programa. Es mejor usar la Inyección de dependencias para eliminar el uso directo de la secuencia de salida estándar: stackoverflow.com/a/21216342/545127
  3. 26

    Lugar de redirigir System.out, me gustaría refactorizar la clase que utiliza System.out.println() pasando un PrintStream como colaborador y, a continuación, utilizando System.out en la producción y un Prueba Espía en la prueba. Es decir, el uso de la Inyección de Dependencia para eliminar el uso directo de la secuencia de salida estándar.

    En La Producción De

    ConsoleWriter writer = new ConsoleWriter(System.out));

    En la Prueba de

    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 en comprobables por un simple refactorización, sin tener la necesidad de indirectos de la redirección de la salida estándar u ocultar la intercepción con una regla del sistema.

    • No puedo encontrar esta ConsoleWriter en cualquier parte del JDK: donde es ?
    • Probablemente debe ser mencionado en la respuesta, pero creo que la clase fue creado por user1909402.
    • Creo que ConsoleWriter es el objeto de la prueba,
  4. 22

    Puede configurar el Sistema.a cabo el flujo de impresión a través de setOut() (y para in y err). Puede redirigir esta a un flujo de impresión que registra a una cadena y, a continuación, compruebe que ? Que parece ser el mecanismo más simple.

    (Yo estaría a favor de, en algún momento, convertir la aplicación de algunas de registro de marco -, pero sospecho que ya son conscientes de esto!)

    • Que fue algo que vino a mi mente, pero no podía creer que no hay ningún estándar de JUnit manera de hacerlo. Gracias, El Cerebro. Pero los créditos consiguió la dfa para el esfuerzo.
    • Este enfoque está plagado de problemas, debido a que el flujo de salida estándar es un recurso compartido utilizado por todas las partes de su programa. Es mejor usar la Inyección de dependencias para eliminar el uso directo de la secuencia de salida estándar: stackoverflow.com/a/21216342/545127
    • Sí. Me gustaría segundo de eso y tal vez incluso la pregunta de un registro assert (mejor para afirmar una llamada a un componente de registro o similar)
  5. 13

    Un poco fuera de tema, pero en el caso de algunas personas (como yo, cuando me enteré de este hilo) podría estar interesado en la captura de registro de salida a través de SLF4J, commons-pruebas‘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:

    • He desarrollado esta biblioteca ya que no pude encontrar ninguna solución adecuada para mis necesidades.
    • Sólo enlaces para log4j, log4j2 y logback están disponibles en el momento, pero estoy feliz de añadir más.
    • Muchas gracias por la creación de esta biblioteca! He estado buscando algo como esto durante mucho tiempo! Es muy, muy útil ya que a veces simplemente no se puede simplificar el código lo suficiente como para ser fácilmente comprobable, pero con un mensaje de registro que usted puede hacer maravillas!!!
    • Esto se ve muy prometedor… pero incluso cuando yo solo copie su ATMTest programa y ejecutarlo como una prueba en virtud de Gradle, me estoy haciendo una excepción… me he planteado un problema en su página de Github…
  6. 9

    @dfa respuesta es genial, así que me tomó un paso más allá para hacer posible los bloques de la prueba de salida.

    Primero he creado TestHelper con un método captureOutput que acepta la annoymous clase CaptureTest. El captureOutput método realiza el trabajo de configuración y derribar los flujos de salida. Cuando la aplicación de CaptureOutput‘s test método se llama, tiene acceso a la salida para generar el bloque de prueba.

    Fuente para 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;
    }

    Nota que TestHelper y CaptureTest se definen en el mismo archivo.

    A continuación, en la prueba, puede importar la estática captureOutput. Aquí hay un ejemplo usando 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() );
                }
            });
    }
  7. 7

    Si se utiliza la Primavera de Arranque (usted mencionó que se está trabajando con una aplicación antigua, por lo que probablemente no, pero podría ser de utilidad para otros), entonces usted podría utilizar org.springframework.el arranque.prueba.de la regla.OutputCapture de la siguiente manera:

    @Rule
    public OutputCapture outputCapture = new OutputCapture();
    
    @Test
    public void out() {
        System.out.print("hello");
        assertEquals(outputCapture.toString(), "hello");
    }
    • I votado su respuesta, porque yo uso la Primavera de arranque y se me puso en la pista de la derecha. Gracias! Sin embargo, outputCapture necesita ser inicializado. (público OutputCapture outputCapture = new OutputCapture();) Consulte docs.de la primavera.io/spring-boot/docs/actual/referencia/html/…
    • Estás absolutamente en lo correcto. Gracias por el comentario! He actualizado mi respuesta.
  8. 3

    Basado en @dfa de la respuesta y otra respuesta que muestra cómo el Sistema de prueba.en, me gustaría compartir mi solución para dar entrada a un programa y poner a prueba su salida.

    Como referencia, puedo usar JUnit 4.12.

    Digamos que tiene este programa que simplemente reproduce la entrada a la salida:

    import java.util.Scanner;
    
    public class SimpleProgram {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            System.out.print(scanner.next());
            scanner.close();
        }
    }

    Para probar, podemos utilizar la siguiente clase:

    import static org.junit.Assert.*;
    
    import java.io.*;
    
    import org.junit.*;
    
    public class SimpleProgramTest {
        private final InputStream systemIn = System.in;
        private final PrintStream systemOut = System.out;
    
        private ByteArrayInputStream testIn;
        private ByteArrayOutputStream testOut;
    
        @Before
        public void setUpOutput() {
            testOut = new ByteArrayOutputStream();
            System.setOut(new PrintStream(testOut));
        }
    
        private void provideInput(String data) {
            testIn = new ByteArrayInputStream(data.getBytes());
            System.setIn(testIn);
        }
    
        private String getOutput() {
            return testOut.toString();
        }
    
        @After
        public void restoreSystemInputOutput() {
            System.setIn(systemIn);
            System.setOut(systemOut);
        }
    
        @Test
        public void testCase1() {
            final String testString = "Hello!";
            provideInput(testString);
    
            SimpleProgram.main(new String[0]);
    
            assertEquals(testString, getOutput());
        }
    }

    No voy a explicar mucho, porque creo que el código sea legible y que he citado en mis fuentes.

    Cuando JUnit se ejecuta testCase1(), se va a llamar los métodos auxiliares en el orden en que aparecen:

    1. setUpOutput(), debido a la @Before anotación
    2. provideInput(String data), llamado de testCase1()
    3. getOutput(), llamado de testCase1()
    4. restoreSystemInputOutput(), debido a la @After anotación

    Yo no prueba System.err porque no lo necesitaba, pero debe ser fácil de implementar, similar a las pruebas System.out.

  9. 2

    Completo JUnit 5 ejemplo para probar System.out (reemplace cuando parte):

    package learning;
    
    import static org.assertj.core.api.BDDAssertions.then;
    
    import java.io.ByteArrayOutputStream;
    import java.io.PrintStream;
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    
    class SystemOutLT {
    
        private PrintStream originalSystemOut;
        private ByteArrayOutputStream systemOutContent;
    
        @BeforeEach
        void redirectSystemOutStream() {
    
            originalSystemOut = System.out;
    
            //given
            systemOutContent = new ByteArrayOutputStream();
            System.setOut(new PrintStream(systemOutContent));
        }
    
        @AfterEach
        void restoreSystemOutStream() {
            System.setOut(originalSystemOut);
        }
    
        @Test
        void shouldPrintToSystemOut() {
    
            //when
            System.out.println("example");
    
            then(systemOutContent.toString()).containsIgnoringCase("example");
        }
    }
  10. 1

    Usted no desea redirigir el sistema.salida de corriente debido a que las redirecciones para TODA la JVM. Todo lo que se ejecuta en la JVM puede llegar en mal estado. Hay maneras mejores de la prueba de entrada/salida. Buscar en los talones/burla.

  11. 0

    Directamente no se puede imprimir mediante el uso de sistema.a cabo.println o el uso de registrador de la api de mientras que el uso de JUnit. Pero si desea comprobar los valores, a continuación, usted simplemente puede usar

    Assert.assertEquals("value", str);

    Va a tirar por debajo de error de aserción:

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

    Su valor debe ser 21.92, Ahora si se prueba utilizando este valor como el siguiente caso de prueba pasará.

     Assert.assertEquals(21.92, str);
  12. 0

    para

    @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 errar

    @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);
    }
    • Para este tipo de instalación y desmontaje de la lógica de que yo iba a usar un @Rule, en lugar de hacerlo en línea en su prueba. En particular, si su aserción falla el segundo System.setOut/Err llamada no ser alcanzado.

Dejar respuesta

Please enter your comment!
Please enter your name here