Ik moet JUNIST-tests schrijven voor een oude applicatie die slecht is ontworpen en schrijft veel foutmeldingen naar de standaarduitvoer. Wanneer de getResponse(String request)
Method gedraagt, retourneert het een XML-reactie:
@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);
}
Maar wanneer het misvormde XML krijgt of het verzoek niet begrijpt, retourneert het null
en schrijft wat spullen naar standaarduitvoer.
Is er een manier om console-uitvoer in Junit te bewaren? Om gevallen van te vangen zoals:
System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
Antwoord 1, Autoriteit 100%
Gebruik bytearrayoutputstream en systeem .Setxxx is eenvoudig:
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);
}
Voorbeeldtestgevallen:
@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());
}
Ik heb deze code gebruikt om de opdrachtregeloptie te testen (beweren dat -versie de versie string, enz.)
bewerken:
Voorafgaand versies van dit antwoord genaamd System.setOut(null)
na de tests; Dit is de oorzaak van NULLPOINTEREXCeptions-commenters verwijzen naar.
Antwoord 2, Autoriteit 16%
Ik weet dat dit een oude draad is, maar er is een mooie bibliotheek om dit te doen: Systeemregels
Voorbeeld van de documenten:
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());
}
}
Hiermee kunt u ook System.exit(-1)
en andere dingen in rekening brengen waarvoor een opdrachtregelgereedschap moet worden getest.
Antwoord 3, Autoriteit 6%
In plaats van het doorverwijzen van System.out
, zou ik de klasse refactor die System.out.println()
gebruikt door een PrintStream
als een medewerker en vervolgens met System.out
in productie en een Test Spy in de test. Dat wil zeggen, gebruik de afhankelijkheidsinjectie om het directe gebruik van de standaarduitgangsstroom te elimineren.
in productie
ConsoleWriter writer = new ConsoleWriter(System.out));
in de test
ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));
Discussie
Op deze manier de klas te testen wordt toetsbaar door een eenvoudige refactoring, zonder de noodzaak voor indirecte omleiding van de standaard-uitgang of obscure onderscheppen met een systeem regel.
Antwoord 4, Autoriteit 3%
U kunt de System.out afdrukstroom ingesteld via uitgezette () (en in
en err
). Kunt u omleiden dit om een afdruk te streamen die opneemt op een string, en dan te inspecteren dat? Dat lijkt de meest eenvoudige mechanisme.
(ik zou pleiten, op een gegeven moment, zet de app om een aantal logging framework – maar ik vermoed dat je al op de hoogte zijn van deze)
Antwoord 5, Autoriteit 2%
Een beetje off topic, maar bij sommige mensen (zoals ik, toen ik voor het eerst deze thread gevonden) misschien wel interesse zou in het vastleggen van log output via SLF4J, commons-testen ‘s JUnit @Rule
kan helpen:
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));
}
}
Disclaimer
- Ik ontwikkelde deze bibliotheek omdat ik geen geschikte oplossing voor mijn eigen behoeften niet kon vinden.
- Alleen bindingen voor
log4j
,log4j2
enlogback
zijn beschikbaar op het moment, maar ik ben blij om meer toe te voegen.
Antwoord 6
@dfa antwoord is geweldig, dus ik nam het een stap verder om te testen blokken van de output mogelijk te maken.
De eerste die ik heb gemaakt TestHelper
met een methode captureOutput
dat de annoymous klasse CaptureTest
accepteert. De captureOutput methode doet het werk van het opzetten en afbreken van de output stromen. Wanneer de uitvoering van captureOutput
‘s test
methode wordt genoemd, heeft toegang tot de uitvoer te genereren voor het testblok.
Bron voor 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;
}
Merk op dat TestHelper en CaptureTest zijn gedefinieerd in hetzelfde bestand.
Toen in uw test, kunt u de statische captureOutput importeren. Hier is een voorbeeld met behulp van 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() );
}
});
}
Antwoord 7
Als u Spring Boot gebruikte (u zei dat u met een oude applicatie werkt, zodat u waarschijnlijk niet bent, maar het kan van nut zijn voor anderen), dan zou u Org.Springframework.boot kunnen gebruiken .test.rule.outputcapture op de volgende manier:
@Rule
public OutputCapture outputCapture = new OutputCapture();
@Test
public void out() {
System.out.print("hello");
assertEquals(outputCapture.toString(), "hello");
}
Antwoord 8
Gebaseerd op @ DFA’s antwoord en Nog een antwoord dat laat zien hoe het systeem moet worden testen.in , wil ik mijn oplossing graag delen om een ingang te geven aan een programma en de uitvoer ervan testen.
Als referentie gebruik ik JUNIST 4.12.
Laten we zeggen dat we dit programma hebben dat eenvoudig de invoer wordt gerepliceerd in uitvoer:
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();
}
}
Om het te testen, kunnen we de volgende klasse gebruiken:
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());
}
}
Ik zal niet veel uitleggen, omdat ik geloof dat de code leesbaar is en ik heb mijn bronnen geciteerd.
Wanneer JUNIST testCase1()
uitvoert, gaat het de helpermethoden bellen in de volgorde waarin ze verschijnen:
setUpOutput()
, vanwege de@Before
annotatieprovideInput(String data)
, opgeroepen vantestCase1()
getOutput()
, opgeroepen vantestCase1()
restoreSystemInputOutput()
, vanwege de@After
annotatie
Ik heb System.err
niet getest omdat ik het niet nodig heb, maar het zou eenvoudig moeten zijn om te implementeren, vergelijkbaar met het testen van System.out
.
Antwoord 9
Volledig Junit 5 Voorbeeld om System.out
te testen (vervang het wanneer deel):
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");
}
}
Antwoord 10
Je wilt de system.out-stream niet omleiden, omdat dat de VOLLEDIGE JVM omleidt. Al het andere dat op de JVM draait, kan in de war raken. Er zijn betere manieren om input/output te testen. Kijk in stubs/mocks.
Antwoord 11
U kunt niet rechtstreeks afdrukken met system.out.printlnof logger apiterwijl u JUnitgebruikt. Maar als u waarden wilt controleren, kunt u gewoon
. gebruiken
Assert.assertEquals("value", str);
Het geeft onderstaande beweringfout:
java.lang.AssertionError: expected [21.92] but found [value]
Uw waarde zou 21,92 moeten zijn. Als u nu test met deze waarde zoals hieronder, zal uw testcase slagen.
Assert.assertEquals(21.92, str);
Antwoord 12
voor uit
@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);
}
voor fout
@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);
}
Antwoord 13
Als de functie afdrukt naar System.out, kunt u die uitvoer vastleggen door de System.setOut-methode te gebruiken om System.out te wijzigen om naar een door u geleverde PrintStream te gaan. Als u een PrintStream maakt die is verbonden met een ByteArrayOutputStream, kunt u de uitvoer vastleggen als een tekenreeks.
// Create a stream to hold the output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
// IMPORTANT: Save the old System.out!
PrintStream old = System.out;
// Tell Java to use your special stream
System.setOut(ps);
// Print some output: goes to your special stream
System.out.println("Foofoofoo!");
// Put things back
System.out.flush();
System.setOut(old);
// Show what happened
System.out.println("Here: " + baos.toString());
Antwoord 14
Hoewel deze vraag erg oud is en al zeer goede antwoorden heeft, wil ik een alternatief bieden. Ik vond het antwoord van dfa
leuk, maar ik wilde iets herbruikbaars hebben in verschillende projecten zonder de configuratie te kopiëren en dus maakte ik er een bibliotheek van en wilde terug bijdragen aan de gemeenschap. Het heet Console Captoren je kunt het toevoegen met het volgende fragment:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>consolecaptor</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
Voorbeeld les
public class FooService {
public void sayHello() {
System.out.println("Keyboard not responding. Press any key to continue...");
System.err.println("Congratulations, you are pregnant!");
}
}
Eenheidstest
import static org.assertj.core.api.Assertions.assertThat;
import nl.altindag.console.ConsoleCaptor;
import org.junit.jupiter.api.Test;
public class FooServiceTest {
@Test
public void captureStandardAndErrorOutput() {
ConsoleCaptor consoleCaptor = new ConsoleCaptor();
FooService fooService = new FooService();
fooService.sayHello();
assertThat(consoleCaptor.getStandardOutput()).contains("Keyboard not responding. Press any key to continue...");
assertThat(consoleCaptor.getErrorOutput()).contains("Congratulations, you are pregnant!");
consoleCaptor.close();
}
}