System.out.println () के लिए JUnit परीक्षा


370

मुझे एक पुराने एप्लिकेशन के लिए JUnit परीक्षण लिखने की ज़रूरत है जो खराब तरीके से डिज़ाइन किया गया है और मानक आउटपुट के लिए बहुत सारे त्रुटि संदेश लिख रहा है। जब getResponse(String request)विधि सही ढंग से व्यवहार करती है तो यह 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);
}

लेकिन जब यह एक्सएमएल विकृत हो जाता है या अनुरोध को समझ नहीं पाता है तो यह nullमानक आउटपुट के लिए कुछ सामान लिखता है।

JUnit में कंसोल आउटपुट को मुखर करने का कोई तरीका है? जैसे मामलों को पकड़ने के लिए:

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

संबंधित, लेकिन stackoverflow.com/questions/3381801/…
Raedwald

जवाबों:


581

ByteArrayOutputStream और System.setXXX का उपयोग करना सरल है:

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

नमूना परीक्षण के मामले:

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

मैंने कमांड कोड विकल्प का परीक्षण करने के लिए इस कोड का उपयोग किया (जो कि -version संस्करण स्ट्रिंग, आदि का आउटपुट देता है)

संपादित करें:System.setOut(null) परीक्षणों के बाद बुलाए गए इस उत्तर के पूर्व संस्करण ; यह NullPointerException टिप्पणीकारों को संदर्भित करने का कारण है।


Furthemore, मैंने प्रतिक्रियाओं के लिए परीक्षण करने के लिए JUnitMatchers का उपयोग किया है: assertThat (परिणाम, इसमें शामिल हैं) ("अनुरोध: GetEmployeeByKeyResponse")); धन्यवाद, dfa
माइक मिनिकि

3
मैं जब वीएम लॉन्च किया गया था तब धारा को वापस लाने के लिए
System.setOut

5
Javadocs System.setOut या System.setErr के लिए अशक्त होने में सक्षम होने के बारे में कुछ नहीं कहते हैं। क्या आप सुनिश्चित हैं कि यह सभी JRE पर काम करेगा?
फाइनेंशियल

55
NullPointerExceptionजैसा कि ऊपर सुझाव दिया गया है ( java.io.writer(Object)एक XML सत्यापनकर्ता द्वारा आंतरिक रूप से कहा जाता है) के रूप में एक अशक्त त्रुटि धारा निर्धारित करने के बाद मुझे अन्य परीक्षणों में सामना करना पड़ा । मैं इसके बजाय मूल को एक क्षेत्र में सहेजने का सुझाव दूंगा: oldStdErr = System.errऔर इसे @Afterविधि में पुनर्स्थापित करना ।
ल्यूक उशरवुड

6
महान समाधान। इसका उपयोग करने वाले किसी भी व्यक्ति के लिए बस एक नोट, आपको आउटकंटेंट से व्हाट्सएप / न्यूलाइन ट्रिम करना होगा।
एलिसन

101

मुझे पता है कि यह एक पुराना धागा है, लेकिन ऐसा करने के लिए एक अच्छा पुस्तकालय है:

सिस्टम नियम

डॉक्स से उदाहरण:

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

यह आपको ट्रैप System.exit(-1)और अन्य चीजों के लिए भी अनुमति देगा जो कमांड लाइन टूल के लिए परीक्षण करने की आवश्यकता होगी।


1
यह दृष्टिकोण समस्याओं से भरा हुआ है क्योंकि मानक आउटपुट स्ट्रीम आपके प्रोग्राम के सभी भागों द्वारा उपयोग किया जाने वाला एक साझा संसाधन है। मानक आउटपुट स्ट्रीम के प्रत्यक्ष उपयोग को समाप्त करने के लिए डिपेंडेंसी इंजेक्शन का उपयोग करना बेहतर है: stackoverflow.com/a/21216342/545127
Raedwald

30

रीडायरेक्ट करने के बजाय System.out, मैं एक सहयोगी के रूप में System.out.println()उत्तीर्ण होकर PrintStreamऔर फिर System.outउत्पादन और परीक्षण में एक परीक्षण जासूस का उपयोग करके उस कक्षा को फिर से तैयार करूंगा । यही है, मानक आउटपुट स्ट्रीम के प्रत्यक्ष उपयोग को समाप्त करने के लिए निर्भरता इंजेक्शन का उपयोग करें।

उत्पादन में

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

टेस्ट में

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

विचार-विमर्श

इस तरह से परीक्षण के तहत कक्षा एक साधारण रीफैक्टरिंग द्वारा परीक्षण योग्य हो जाती है, बिना किसी मानक नियम के मानक आउटपुट या अस्पष्ट अवरोधन के अप्रत्यक्ष पुनर्निर्देशन की आवश्यकता के बिना।


1
मैं JDK में कहीं भी इस ConsoleWriter को नहीं खोज पाया: यह कहाँ है?
जीन-फिलिप कारुआना

3
इसका उत्तर में उल्लेख किया जाना चाहिए, लेकिन मेरा मानना ​​है कि वर्ग user1909402 द्वारा बनाया गया था।
सेबस्टियन

6
मुझे लगता ConsoleWriterहै कि परीक्षा का विषय है,
नील डे वेट

22

आप के माध्यम से System.out प्रिंट धारा सेट कर सकते हैं setout () (और के लिए inऔर err)। क्या आप इसे एक प्रिंट स्ट्रीम पर पुनर्निर्देशित कर सकते हैं जो एक स्ट्रिंग को रिकॉर्ड करता है, और फिर उसका निरीक्षण करता है? यह सबसे सरल तंत्र प्रतीत होता है।

(मैं किसी चरण में वकालत करूंगा, एप्लिकेशन को कुछ लॉगिंग फ्रेमवर्क में बदल दूंगा - लेकिन मुझे संदेह है कि आप पहले से ही इसके बारे में जानते हैं!)


1
यह कुछ ऐसा था जो मेरे दिमाग में आया था, लेकिन मुझे विश्वास नहीं हो रहा था कि ऐसा करने के लिए कोई मानक JUnit तरीका नहीं है। धन्यवाद, मस्तिष्क। लेकिन वास्तविक प्रयास के लिए क्रेडिट dfa को मिला।
मिनिट पर माइक मिनिकी जूल

यह दृष्टिकोण समस्याओं से भरा हुआ है क्योंकि मानक आउटपुट स्ट्रीम आपके प्रोग्राम के सभी भागों द्वारा उपयोग किया जाने वाला एक साझा संसाधन है। मानक आउटपुट स्ट्रीम के प्रत्यक्ष उपयोग को समाप्त करने के लिए डिपेंडेंसी इंजेक्शन का उपयोग करना बेहतर है: stackoverflow.com/a/21216342/545127
Raedwald

हाँ। मैं दूसरा होगा और शायद एक लॉगिंग एसर (एक लॉगिंग घटक या इसी तरह की कॉल पर जोर देने के लिए बेहतर) पर सवाल
ब्रायन एग्न्यू

13

विषय से थोड़ा हटकर, लेकिन कुछ लोग (जैसे, जब मुझे पहली बार यह धागा मिला) SLF4J के माध्यम से लॉग आउटपुट कैप्चर करने में रुचि हो सकती है, कॉमन्स-टेस्टिंग JUnit @Ruleमदद कर सकता है:

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

अस्वीकरण :

  • मैंने इस पुस्तकालय को विकसित किया क्योंकि मुझे अपनी आवश्यकताओं के लिए कोई उपयुक्त समाधान नहीं मिला।
  • केवल फिलहाल के लिए बाइंडिंग log4j, log4j2और logbackउपलब्ध हैं, लेकिन मैं और अधिक जोड़ने के लिए खुश हूं।

इस लाइब्रेरी को बनाने के लिए बहुत बहुत धन्यवाद! मैं इतने लंबे समय से इस तरह की चीज़ की तलाश में था! यह बहुत उपयोगी है, कभी-कभी आप बस अपने कोड को सरलता से परीक्षण करने के लिए पर्याप्त नहीं बना सकते हैं, लेकिन एक लॉग संदेश के साथ आप चमत्कार कर सकते हैं!
carlspring

यह वास्तव में आशाजनक लग रहा है ... लेकिन यहां तक ​​कि जब मैं सिर्फ आपके एटीएमटेस्ट प्रोग्राम की प्रतिलिपि बनाता हूं और इसे ग्रैडल के तहत एक परीक्षण के रूप में चलाता हूं, तो मुझे एक अपवाद मिल रहा है ... मैंने आपके जीथब पृष्ठ पर एक मुद्दा उठाया है ...
माइक कृंतक

9

@dfa का उत्तर बहुत अच्छा है, इसलिए मैंने इसे ouput के ब्लॉक का परीक्षण करना संभव बनाने के लिए एक कदम आगे बढ़ाया।

पहले मैंने TestHelperएक विधि के साथ बनाया captureOutputजो कष्टप्रद वर्ग को स्वीकार करता है CaptureTest। कैप्चरऑउटपुट विधि आउटपुट स्ट्रीम को सेट करने और फाड़ने का काम करती है। जब CaptureOutput's' testपद्धति को लागू किया जाता है, तो इसका परीक्षण ब्लॉक के लिए आउटपुट जनरेट तक पहुंच होती है।

टेस्ट हेल्पर के लिए स्रोत:

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

ध्यान दें कि TestHelper और CaptureTest एक ही फ़ाइल में परिभाषित किए गए हैं।

तब आपके परीक्षण में, आप स्थैतिक कैप्चरटूट आयात कर सकते हैं। यहाँ 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

यदि आप स्प्रिंग बूट का उपयोग कर रहे थे (आपने उल्लेख किया है कि आप एक पुराने एप्लिकेशन के साथ काम कर रहे हैं, तो आप शायद नहीं हैं लेकिन यह दूसरों के लिए उपयोग हो सकता है), तो आप org.springframework.boot.test.rule.OutputCapture का उपयोग कर सकते हैं निम्नलिखित तरीके से:

@Rule
public OutputCapture outputCapture = new OutputCapture();

@Test
public void out() {
    System.out.print("hello");
    assertEquals(outputCapture.toString(), "hello");
}

1
मैंने आपके उत्तर को वोट दिया क्योंकि मैं स्प्रिंग बूट का उपयोग करता हूं और इसने मुझे सही रास्ते पर स्थापित किया है। धन्यवाद! हालाँकि, outputCapture को प्रारंभ करने की आवश्यकता है। (सार्वजनिक OutputCapture outputCapture नई OutputCapture () =;) देखें docs.spring.io/spring-boot/docs/current/reference/html/...
EricGreg

आप बिल्कुल सही हैं। टिप्पणी के लिए धन्यवाद! मैंने अपना उत्तर अपडेट कर दिया है।
Disper

4

@ Dfa के उत्तर और एक अन्य उत्तर के आधार पर, जो दिखाता है कि System.in का परीक्षण कैसे किया जाता है , मैं किसी प्रोग्राम को इनपुट देने और उसके आउटपुट का परीक्षण करने के लिए अपना समाधान साझा करना चाहूंगा।

एक संदर्भ के रूप में, मैं JUnit 4.12 का उपयोग करता हूं।

मान लें कि हमारे पास यह प्रोग्राम है जो केवल आउटपुट के इनपुट की नकल करता है:

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

इसका परीक्षण करने के लिए, हम निम्न वर्ग का उपयोग कर सकते हैं:

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

मैं ज्यादा नहीं समझाऊंगा, क्योंकि मेरा मानना ​​है कि कोड पठनीय है और मैंने अपने स्रोतों का हवाला दिया।

जब JUnit चलता है testCase1(), तो यह सहायक तरीकों को कॉल करने जा रहा है, जिस क्रम में वे दिखाई देते हैं:

  1. setUpOutput(), @Beforeएनोटेशन के कारण
  2. provideInput(String data)कहा जाता है testCase1()
  3. getOutput()कहा जाता है testCase1()
  4. restoreSystemInputOutput(), @Afterएनोटेशन के कारण

मैंने परीक्षण नहीं किया System.errक्योंकि मुझे इसकी आवश्यकता नहीं थी, लेकिन परीक्षण के समान इसे लागू करना आसान होना चाहिए System.out


1

आप System.out स्ट्रीम को पुनर्निर्देशित नहीं करना चाहते हैं क्योंकि यह ENTIRE JVM के लिए पुनर्निर्देशित करता है। जेवीएम पर चलने वाली कोई भी चीज़ गड़बड़ हो सकती है। इनपुट / आउटपुट का परीक्षण करने के बेहतर तरीके हैं। स्टब्स / मोक्स में देखें।


1

पूर्ण JUnit 5 उदाहरण परीक्षण करने के लिए System.out(जब हिस्सा बदलें):

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

आप सीधे JUnit का उपयोग करते समय system.out.println या लकड़हारा एपीआई का उपयोग करके प्रिंट नहीं कर सकते । लेकिन अगर आप किसी भी मूल्य की जांच करना चाहते हैं तो आप बस उपयोग कर सकते हैं

Assert.assertEquals("value", str);

यह जोर त्रुटि के नीचे फेंक देंगे:

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

आपका मान 21.92 होना चाहिए, अब यदि आप इस मान का उपयोग करके परीक्षण करेंगे जैसे कि आपका परीक्षण मामला नीचे से गुजरेगा।

 Assert.assertEquals(21.92, str);

0

बाहर के लिए

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

गलती के लिए

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

इस तरह के सेटअप और टैडडाउन लॉजिक के लिए मैं @Ruleआपके टेस्ट में इनलाइन करने के बजाय एक का उपयोग करता हूं । विशेष रूप से, यदि आपका दावा विफल रहता है तो दूसरी System.setOut/Errकॉल नहीं पहुंचेगी।
dimo414
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.