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());
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 à.
je sais que c'est un vieux fil, mais il y a une belle bibliothèque pour le faire:
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.
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!)
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.
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
etlogback
sont disponibles pour le moment, mais je suis heureux d'ajouter plus.
@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() );
}
});
}
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");
}
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.
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);
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);
}
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");
}
}
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:
-
setUpOutput()
, en raison de la@Before
annotation -
provideInput(String data)
, appelé detestCase1()
-
getOutput()
, appelé detestCase1()
-
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
.