unit test runwith run example java console junit

java - test - Prueba de JUnit para System.out.println()



run unit test java command line (12)

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 entrada a un programa y probar su salida.

Como referencia, uso JUnit 4.12.

Digamos que tenemos este programa que simplemente replica entrada a 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 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 explicaré mucho porque creo que el código es legible y cité mis fuentes.

Cuando JUnit ejecuta testCase1() , llamará a los métodos auxiliares 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 System.err porque no lo necesitaba, pero debería ser fácil de implementar, similar a probar System.out .

Necesito escribir pruebas de 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 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 un formato XML incorrecto o no comprende la solicitud, devuelve un null y escribe algunas cosas en la salida estándar.

¿Hay alguna manera 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());


Ejemplo completo de JUnit 5 para probar System.out (reemplazar la parte cuándo):

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


En lugar de redirigir System.out , refactorizaría la clase que usa System.out.println() al pasar un PrintStream como colaborador y luego usar System.out en producción y un Test Spy en la prueba. Es decir, use 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 puede probar mediante una refactorización simple, sin tener la necesidad de redireccionar indirectamente la salida estándar o la interceptación oscura con una regla del sistema.


La respuesta de @dfa es genial, así que fui un paso más allá para hacer posible probar bloques de salida.

Primero creé TestHelper con un método captureOutput que acepta la clase molesta CaptureTest . El método captureOutput hace el trabajo de configurar y eliminar los flujos de salida. Cuando se llama a la implementación del método de test de CaptureOutput , 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 están definidos en el mismo archivo.

Luego, en su prueba, puede importar la captura de salida estática. 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() ); } }); }


No desea redireccionar la secuencia de system.out porque eso redirecciona la JVM COMPLETA. Cualquier otra cosa que se ejecute en la JVM puede ensuciarse. Hay mejores maneras de probar la entrada / salida. Busque en los talones / mocks.


No puede imprimir directamente usando system.out.println o usando la api del registrador mientras usa JUnit . Pero si desea verificar cualquier valor, simplemente puede usar

Assert.assertEquals("value", str);

Se lanzará debajo del error de aserción:

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

Su valor debería ser 21.92. Ahora, si va a probar usando este valor, como pasará, su caso de prueba pasará.

Assert.assertEquals(21.92, str);


Puede configurar el flujo de impresión System.out a través de setOut() (y para in y err ). ¿Puede redirigir esto a un flujo de impresión que registra a una cadena y luego inspeccionarlo? Ese parece ser el mecanismo más simple.

(Yo recomendaría, en algún momento, convertir la aplicación a algún marco de registro, pero sospecho que ya está al tanto de esto).


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

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á interceptar System.exit(-1) y otras cosas que una herramienta de línea de comandos debería probar.


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 de utilidad para otros), entonces podría usar org.springframework.boot.test.rule.OutputCapture en la siguiente manera:

@Rule public OutputCapture outputCapture = new OutputCapture(); @Test public void out() { System.out.print("hello"); assertEquals(outputCapture.toString(), "hello"); }


Un poco fuera del tema, pero en caso de que algunas personas (como yo, cuando encontré este hilo por primera vez) pudieran estar interesadas en capturar la salida del registro a través de SLF4J, JUnit @Rule commons-testing 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 podía encontrar ninguna solución adecuada para mis propias necesidades.
  • Solo los enlaces para log4j , log4j2 y logback están disponibles en este momento, pero estoy feliz de agregar más.

el uso de 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 la línea de comandos (afirmando que -version genera la cadena de versión, etc., etc.)

Edición: las versiones anteriores de esta respuesta llamadas System.setOut(null) después de las pruebas; Esta es la causa de que los comentaristas de NullPointerExceptions se refieren a.


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

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