Test de JUnit pour le système.hors.println()

j'ai besoin d'écrire des tests JUnit pour une ancienne application qui est mal conçue et écrit beaucoup de messages d'erreur à la sortie standard. Lorsque la méthode getResponse(String request) se comporte correctement, elle renvoie une réponse 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);
}

mais quand il obtient XML malformé ou ne comprend pas la requête il renvoie null et écrit quelques trucs à la sortie standard.

est-il possible d'affirmer la sortie de la console en JUnit? Pour attraper des cas comme:

System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
289
demandé sur Bonifacio2 2009-07-13 17:18:13

12 réponses

utilisant ByteArrayOutputStream et système.setXXX est 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);
}

exemples de cas d'essai:

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

j'ai utilisé ce code pour tester l'option de ligne de commande (affirmant que-version sort la chaîne de version, etc etc)

Edit: Versions antérieures de cette réponse appelée System.setOut(null) après les tests; C'est la cause de NullPointerExceptions commentateurs reportez-vous à.

476
répondu dfa 2018-06-21 10:22:13

je sais que c'est un vieux fil, mais il y a une belle bibliothèque pour le faire:

Système De Règles

exemple tiré du docs:

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

, Il vous permettra également de piège System.exit(-1) et d'autres choses qu'un outil de ligne de commande devrait être testée pour.

84
répondu Will 2015-09-25 07:11:14

vous pouvez configurer le système.sortie du flux d'impression via setOut () (et pour in et err ). Pouvez-vous rediriger un flux d'impression qui enregistre sur une chaîne, puis vérifiez que ? Qui semble être le plus simple mécanisme.

(Je préconiserais, à un certain stade, de convertir l'application en un certain cadre de journalisation - mais je soupçonne que vous êtes déjà au courant de cela!)

21
répondu Brian Agnew 2009-07-13 13:19:46

au lieu de rediriger System.out , Je refacterais la classe qui utilise System.out.println() en passant un PrintStream comme collaborateur et en utilisant ensuite System.out en production et un Espion D'essai dans le test.

En Production

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

dans le Test

ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));

Discussion

de cette façon la classe sous test devient testable par un simple remaniement, sans avoir besoin de redirection indirecte de la sortie standard ou d'interception obscure avec une règle de système.

16
répondu user1909402 2014-01-19 11:49:31

légèrement hors sujet, mais dans le cas où certaines personnes (comme moi, quand j'ai trouvé ce fil) pourraient être intéressés à capturer la production log via SLF4J, commons-testing 's JUnit @Rule might help:

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

clause de non-responsabilité :

  • j'ai développé cette bibliothèque car je ne pouvais trouver aucune solution appropriée pour mes propres besoins.
  • uniquement les reliures pour log4j , log4j2 et logback sont disponibles pour le moment, mais je suis heureux d'ajouter plus.
10
répondu Marc Carré 2014-12-11 08:29:34

@Dfa la réponse est géniale, donc j'ai fait un pas de plus pour qu'il soit possible de tester des blocs d'ouput.

J'ai d'abord créé TestHelper avec une méthode captureOutput qui accepte la classe ennuyeuse CaptureTest . La méthode de captureOutput effectue le travail de mise en place et de démontage des flux de sortie. Lorsque l'implémentation de la méthode CaptureOutput 's test est appelée, elle a accès à la sortie générer pour le bloc test.

Source pour 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;
}

notez que TestHelper et CaptureTest sont définis dans le même fichier.

puis dans votre test, vous pouvez importer le captureOutput statique. Voici un exemple d'utilisation de 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
répondu mguymon 2012-09-02 06:07:04

si vous utilisiez une botte à ressort (vous avez mentionné que vous travaillez avec une ancienne application, donc vous ne l'êtes probablement pas mais elle pourrait être utile à d'autres), alors vous pourriez utiliser org.springframework.démarrage.test.règle.OutputCapture de la manière suivante:

@Rule
public OutputCapture outputCapture = new OutputCapture();

@Test
public void out() {
    System.out.print("hello");
    assertEquals(outputCapture.toString(), "hello");
}
6
répondu Disper 2017-03-25 07:47:16

Vous ne voulez pas rediriger le système.out stream parce que cela redirige pour L'ensemble de la JVM. Tout ce qui fonctionne sur le JVM peut être foutu. Il y a de meilleures façons de tester les entrées/sorties. Regardez dans stubs / mocks.

1
répondu Sam Jacobs 2012-10-13 09:10:53

vous ne pouvez pas imprimer directement en utilisant le système .hors.println ou à l'aide de enregistreur de api lors de l'utilisation JUnit . Mais si vous voulez vérifier toutes les valeurs, alors vous pouvez simplement utiliser

Assert.assertEquals("value", str);

qu'Il va jeter ci-dessous erreur d'assertion:

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

votre valeur devrait être 21.92, maintenant si vous testerez en utilisant cette valeur comme ci-dessous votre cas de test réussira.

 Assert.assertEquals(21.92, str);
1
répondu Aftab Virtual 2016-07-19 05:56:34

pour

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

pour 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
répondu Shimon Doodkin 2017-05-04 20:15:18

Plein JUnit 5 exemple de test System.out (remplacer lors de la partie):

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
répondu Jens Piegsa 2018-05-31 12:31:32

basé sur la réponse de @dfa et une autre réponse qui montre comment tester le système.dans , je voudrais partager ma solution pour donner une entrée à un programme et tester sa sortie.

comme référence, J'utilise JUnit 4.12.

disons que nous avons ce programme qui reproduit simplement input to output:

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

pour le tester, nous pouvons utiliser la classe suivante:

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

Je n'expliquerai pas grand-chose, parce que je crois que le code est lisible et j'ai cité mes sources.

quand JUnit lance testCase1() , il va appeler les méthodes d'aide dans l'ordre où elles apparaissent:

  1. setUpOutput() , en raison de la @Before annotation
  2. provideInput(String data) , appelé de testCase1()
  3. getOutput() , appelé de testCase1()
  4. restoreSystemInputOutput() , en raison de la @After annotation

Je n'ai pas testé System.err parce que je n'en avais pas besoin, mais il devrait être facile à mettre en œuvre, similaire au test System.out .

0
répondu Antonio Vinicius Menezes Medei 2018-06-06 13:09:28