JUnit test for System.out.println()

前端 未结 13 1693
广开言路
广开言路 2020-11-22 03:18

I need to write JUnit tests for an old application that\'s poorly designed and is writing a lot of error messages to standard output. When the getResponse(String reque

相关标签:
13条回答
  • 2020-11-22 03:57

    Slightly off topic, but in case some people (like me, when I first found this thread) might be interested in capturing log output 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));
        }
    }
    

    Disclaimer:

    • I developed this library since I could not find any suitable solution for my own needs.
    • Only bindings for log4j, log4j2 and logback are available at the moment, but I am happy to add more.
    0 讨论(0)
  • 2020-11-22 04:01

    using ByteArrayOutputStream and System.setXXX is 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);
    }
    

    sample test cases:

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

    I used this code to test the command line option (asserting that -version outputs the version string, etc etc)

    Edit: Prior versions of this answer called System.setOut(null) after the tests; This is the cause of NullPointerExceptions commenters refer to.

    0 讨论(0)
  • 2020-11-22 04:03

    Based on @dfa's answer and another answer that shows how to test System.in, I would like to share my solution to give an input to a program and test its output.

    As a reference, I use JUnit 4.12.

    Let's say we have this program that simply replicates 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();
        }
    }
    

    To test it, we can use the following class:

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

    I won't explain much, because I believe the code is readable and I cited my sources.

    When JUnit runs testCase1(), it is going to call the helper methods in the order they appear:

    1. setUpOutput(), because of the @Before annotation
    2. provideInput(String data), called from testCase1()
    3. getOutput(), called from testCase1()
    4. restoreSystemInputOutput(), because of the @After annotation

    I didn't test System.err because I didn't need it, but it should be easy to implement, similar to testing System.out.

    0 讨论(0)
  • 2020-11-22 04:10

    You can set the System.out print stream via setOut() (and for in and err). Can you redirect this to a print stream that records to a string, and then inspect that ? That would appear to be the simplest mechanism.

    (I would advocate, at some stage, convert the app to some logging framework - but I suspect you already are aware of this!)

    0 讨论(0)
  • 2020-11-22 04:10

    You don't want to redirect the system.out stream because that redirects for the ENTIRE JVM. Anything else running on the JVM can get messed up. There are better ways to test input/output. Look into stubs/mocks.

    0 讨论(0)
  • 2020-11-22 04:12

    I know this is an old thread, but there is a nice library to do this:

    System Rules

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

    It will also allow you to trap System.exit(-1) and other things that a command line tool would need to be tested for.

    0 讨论(0)
提交回复
热议问题