Prueba JUnit para el sistema.fuera.println()


Necesito escribir pruebas JUnit para una aplicación antigua que está mal diseñada y está escribiendo muchos mensajes de error en la salida estándar. Cuando el método getResponse(String request) 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 XML mal formado o no entiende la solicitud, devuelve null y escribe algunas cosas en la salida estándar.

¿Hay alguna forma de afirmar la salida de la consola en JUnit? Para atrapar casos como:

System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
Author: Bonifacio2, 2009-07-13

12 answers

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

Usé este código para probar la opción de línea de comandos (afirmando que-version muestra la cadena de versión, etc etc)

Editar: Versiones anteriores de esta respuesta llamada System.setOut(null) después de las pruebas; Esta es la causa de NullPointerExceptions comentaristas se refieren a.

 476
Author: dfa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-21 10:22:13

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

Reglas del sistema

Ejemplo 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á atrapar System.exit(-1) y otras cosas para las que una herramienta de línea de comandos tendría que ser probada.

 84
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-25 07:11:14

Puede configurar el Sistema.out print stream vía setOut() (y para in y err). ¿Puede redirigir esto a un flujo de impresión que registra una cadena y luego inspeccionarlo ? Ese parece ser el mecanismo más sencillo.

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

 21
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-13 13:19:46

En lugar de redireccionar System.out, refactorizaría la clase que usa System.out.println() pasando a PrintStream como colaborador y luego usando System.out en producción y a Test Spy en 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 vuelve comprobable por una simple refactorización, sin tener la necesidad de redirección indirecta de la salida estándar o intercepción oscura con un regla del sistema.

 16
Author: user1909402,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-19 11:49:31

Un poco fuera de tema, pero en caso de que algunas personas (como yo, cuando encontré este hilo por primera vez) podrían estar interesadas en capturar la salida de registro a través de SLF4J, commons-testing 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 ya que no pude encontrar ninguna solución adecuada para mis propias necesidades.
  • Solo vinculaciones para log4j, log4j2 y logback están disponibles en este momento, pero estoy feliz de añadir más.
 10
Author: Marc Carré,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-11 08:29:34

@dfa la respuesta es genial, así que lo llevé un paso más allá para hacer posible probar bloques de salida.

Primero creé TestHelper con un método captureOutput que acepta la clase annoymous CaptureTest. El método captureOutput hace el trabajo de establecer y derribar los flujos de salida. Cuando se llama a la implementación del método CaptureOutput's test, tiene acceso a la salida generada para 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;
}

Tenga en cuenta que TestHelper y CaptureTest son definido en el mismo archivo.

Luego, en su prueba, puede importar el captureOutput estático. 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
Author: mguymon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-02 06:07:04

Si estaba usando Spring Boot (mencionó que está trabajando con una aplicación antigua, por lo que probablemente no lo esté, pero podría ser útil para otros), entonces podría usar org.springframework.arranque.prueba.regla.OutputCapture de la siguiente manera:

@Rule
public OutputCapture outputCapture = new OutputCapture();

@Test
public void out() {
    System.out.print("hello");
    assertEquals(outputCapture.toString(), "hello");
}
 6
Author: Disper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-25 07:47:16

No desea redirigir el sistema.out stream porque redirige para TODA la JVM. Cualquier otra cosa que se ejecute en la JVM puede estropearse. Hay mejores maneras de probar la entrada / salida. Mira en talones / burlas.

 1
Author: Sam Jacobs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-13 09:10:53

No se puede imprimir directamente usando el sistema .fuera.println o usando logger api mientras se usa JUnit. Pero si desea verificar cualquier valor, simplemente puede usar

Assert.assertEquals("value", str);

Lanzará debajo del 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 debajo de su caso de prueba pasará.

 Assert.assertEquals(21.92, str);
 1
Author: Aftab Virtual,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-19 05:56:34

For out

@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);
}
 0
Author: Shimon Doodkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-04 20:15:18

Ejemplo completo de JUnit 5 para probar System.out (reemplace la parte when):

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");
    }
}
 0
Author: Jens Piegsa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-31 12:31:32

Basado en la respuesta de @dfa y otra respuesta que muestra cómo probar System.in , me gustaría compartir mi solución para dar una entrada a un programa y probar su salida.

Como referencia, utilizo JUnit 4.12.

Digamos que tenemos este programa que simplemente replica la entrada a la salida:{[18]]}

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 probarlo, podemos usar 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 es legible y cité mi fuente.

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

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

No probé System.err porque no lo necesitaba, pero debería ser fácil de implementar, similar a probar System.out.

 0
Author: Antonio Vinicius Menezes Medei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-06 13:09:28