446 Stimmen

JUnit-Test für System.out.println()

Ich muss JUnit-Tests für eine alte Anwendung schreiben, die schlecht entworfen ist und eine Menge Fehlermeldungen in die Standardausgabe schreibt. Wenn die getResponse(String request) Methode verhält sich korrekt und liefert eine XML-Antwort:

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

Wenn er jedoch fehlerhaftes XML erhält oder die Anfrage nicht versteht, gibt er zurück null und schreibt einige Dinge in die Standardausgabe.

Gibt es eine Möglichkeit, die Konsolenausgabe in JUnit zu bestätigen? Um Fälle wie zu fangen:

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

700voto

dfa Punkte 110809

Mit ByteArrayOutputStream und System.setXXX ist einfach:

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

Beispiel-Testfälle:

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

Ich habe diesen Code verwendet, um die Befehlszeilenoption zu testen (um sicherzustellen, dass -version die Versionszeichenfolge ausgibt, usw.)

Edita: Frühere Versionen dieser Antwort nannten System.setOut(null) nach den Tests; dies ist die Ursache der NullPointerExceptions, auf die sich die Kommentatoren beziehen.

108voto

Will Punkte 5945

Ich weiß, dies ist ein altes Thema, aber es gibt eine schöne Bibliothek, um dies zu tun: Systemregeln
Beispiel aus der Dokumentation:

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

Außerdem können Sie damit Folgendes einfangen System.exit(-1) und andere Dinge, auf die ein Kommandozeilenprogramm getestet werden müsste.

42voto

user1909402 Punkte 441

Statt einer Umleitung System.out würde ich die Klasse umstrukturieren, die System.out.println() durch Übergabe einer PrintStream als Kollaborateur und dann mit System.out in der Produktion und ein Test Spion im Test. Verwenden Sie also Dependency Injection, um die direkte Verwendung des Standardausgabestroms zu vermeiden.

In Produktion

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

Im Test

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

Diskussion

Auf diese Weise wird die zu testende Klasse durch ein einfaches Refactoring testbar, ohne dass eine indirekte Umleitung der Standardausgabe oder ein obskures Abfangen mit einer Systemregel erforderlich ist.

22voto

Brian Agnew Punkte 260470

Sie können den System.out-Druckstrom über setOut() (und für in et err ). Können Sie dies in einen Druckstrom umleiten, der eine Zeichenkette aufzeichnet, und diese dann untersuchen? Das scheint der einfachste Mechanismus zu sein.

(Ich würde dafür plädieren, die App irgendwann in ein Logging-Framework zu konvertieren - aber ich vermute, Sie sind sich dessen bereits bewusst!)

13voto

Marc Carré Punkte 1407

Das ist zwar nicht ganz das Thema, aber für den Fall, dass einige Leute (wie ich, als ich diesen Thread zum ersten Mal fand) daran interessiert sein könnten, Log-Ausgaben über SLF4J zu erfassen, commons-testing JUnit @Rule könnte helfen:

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

Haftungsausschluss :

  • Ich habe diese Bibliothek entwickelt, da ich keine geeignete Lösung für meine eigenen Bedürfnisse finden konnte.
  • Nur Bindungen für log4j , log4j2 et logback sind im Moment verfügbar, aber ich bin gerne bereit, weitere hinzuzufügen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X