Test JUnit dla systemu.Wynocha.println()
Muszę napisać testy JUnit dla starej aplikacji, która jest źle zaprojektowana i pisze wiele komunikatów o błędach na standardowe wyjście. Gdy metoda getResponse(String request)
zachowuje się poprawnie, zwraca odpowiedź 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);
}
Ale gdy XML zostanie źle sformatowany lub nie zrozumie żądania, zwraca null
i zapisuje pewne rzeczy na standardowe wyjście.
Czy jest jakiś sposób na potwierdzenie wyjścia konsoli w JUnit? Do łapania takich przypadków jak:
System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
12 answers
Za pomocą ByteArrayOutputStream i systemu.setXXX jest prosty:
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);
}
Przykładowe przypadki testowe:
@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());
}
Użyłem tego kodu do przetestowania opcji wiersza poleceń (twierdząc, że-version wyświetla ciąg wersji, itd itp)
Edit:
Wcześniejsze wersje tej odpowiedzi nazwane System.setOut(null)
po testach; jest to przyczyna NullPointerExceptions, do której odnoszą się komentatorzy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-21 10:22:13
Wiem, że to stary wątek, ale jest do tego fajna biblioteka:
Przykład z dokumentów:
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());
}
}
Pozwoli również na pułapkę System.exit(-1)
i inne rzeczy, dla których narzędzie wiersza poleceń musi być przetestowane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 07:11:14
Możesz ustawić System.out print stream poprzez setOut () (oraz dla in
i err
). Czy możesz przekierować to do strumienia drukowania, który zapisuje się na ciąg znaków, a następnie sprawdzić to ? To wydaje się być najprostszy mechanizm.
(chciałbym, na pewnym etapie, przekonwertować aplikację do jakiegoś frameworka logowania - ale podejrzewam, że już o tym wiesz!)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-13 13:19:46
Zamiast przekierowywać System.out
, refaktorowałbym klasę, która używa System.out.println()
, przekazując PrintStream
jako współpracownika, a następnie używając System.out
w produkcji i szpiega testowego W teście.
W Produkcji
ConsoleWriter writer = new ConsoleWriter(System.out));
W teście
ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));
Dyskusja
W ten sposób testowana klasa staje się testowalna przez prostą refaktoryzację, bez potrzeby pośredniego przekierowania standardowego wyjścia lub przechwytywania za pomocą zasada systemowa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-19 11:49:31
Nieco off topic, ale w przypadku, gdy niektórzy ludzie (jak ja, kiedy po raz pierwszy znalazłem ten wątek) mogą być zainteresowani przechwytywaniem logów przez SLF4J, JUnitcommons-testing może pomóc:
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));
}
}
Zastrzeżenie :
- stworzyłem tę bibliotekę, ponieważ nie mogłem znaleźć żadnego odpowiedniego rozwiązania dla moich własnych potrzeb.
- tylko wiązania dla
log4j
,log4j2
ilogback
są dostępne w tej chwili, ale z przyjemnością dodam więcej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-11 08:29:34
@Dfa odpowiedź jest świetna, więc zrobiłem krok dalej, aby umożliwić testowanie bloków ouput.
Najpierw stworzyłem {[2] } metodą captureOutput
, która akceptuje klasę CaptureTest
. Metoda captureOutput wykonuje pracę polegającą na ustawianiu i rozkładaniu strumieni wyjściowych. Po wywołaniu implementacji metody CaptureOutput
s test
, ma ona dostęp do danych wyjściowych generowanych dla bloku testowego.
Źródło dla 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;
}
Zauważ, że TestHelper i CaptureTest są zdefiniowane w tym samym pliku.
Następnie w teście możesz zaimportować static captureOutput. Oto przykład użycia 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() );
}
});
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-02 06:07:04
Jeśli używasz Spring Boot (wspomniałeś, że pracujesz ze starą aplikacją, więc prawdopodobnie nie jesteś, ale może być przydatna dla innych), Możesz użyć org.springframework.but.test.zasada.OutputCapture w następujący sposób:
@Rule
public OutputCapture outputCapture = new OutputCapture();
@Test
public void out() {
System.out.print("hello");
assertEquals(outputCapture.toString(), "hello");
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-25 07:47:16
Nie chcesz przekierowywać systemu.out stream, ponieważ przekierowuje dla całego JVM. Wszystko inne działa na JVM może się popsuć. Istnieją lepsze sposoby testowania wejścia/wyjścia. Zajrzyj do stubs/mocks.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-13 09:10:53
Nie można bezpośrednio wydrukować za pomocą systemu .Wynocha.println lub używając logger api Podczas używania JUnit . Ale jeśli chcesz sprawdzić jakieś wartości, możesz po prostu użyć
Assert.assertEquals("value", str);
Rzuci poniżej błąd twierdzenia:
java.lang.AssertionError: expected [21.92] but found [value]
Twoja wartość powinna wynosić 21.92, teraz jeśli będziesz testował używając tej wartości, jak poniżej, Twój przypadek testowy przejdzie.
Assert.assertEquals(21.92, str);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-19 05:56:34
Na zewnątrz
@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);
}
Dla 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-04 20:15:18
Pełny przykład JUnit 5 do testu System.out
(zastąp część 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");
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 12:31:32
Na podstawie @dfa ' s answer i kolejna odpowiedź, która pokazuje jak testować System.in , chciałbym podzielić się moim rozwiązaniem, aby dać wejście do programu i przetestować jego wyjście.
Jako odniesienie używam JUnit 4.12.
Załóżmy, że mamy ten program, który po prostu replikuje wejście do wyjścia:
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();
}
}
Aby go przetestować, możemy użyć następującej klasy:
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());
}
}
Niewiele wyjaśnię, ponieważ uważam, że kod jest czytelny i zacytowałem mój źródła.
Gdy JUnit uruchomi testCase1()
, wywoła metody pomocnicze w kolejności, w jakiej się pojawiają:
-
setUpOutput()
, z powodu@Before
adnotacji -
provideInput(String data)
, wywołane ztestCase1()
-
getOutput()
, wywołane ztestCase1()
-
restoreSystemInputOutput()
, z powodu@After
adnotacji
Nie testowałem System.err
, ponieważ go nie potrzebowałem, ale powinien być łatwy do wdrożenia, podobny do testowania System.out
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-06 13:09:28