Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

This code allows calling a method while redirecting the IO to strings. This is useful for unit testing where you have to direct something to the console or examine output that comes from it. I assume these have been written a lot, but I couldn't find examples.

I'd like advice on how to make them generally useful and whether the style seems decent. There are two methods-- one sends a string to System.in while leaving the output alone, the other redirects both input and output, with the option that if the input string passed is null, it leaves the input alone. This covers all three cases.

Invocations look like this:

enterInput(inputString, callingClassString, methodString, typeStrings[], argsForCall ...);
captureIO(inputString, callingClassString, methodString, typeStrings[], argsForCall ...);

Code:

/**
 * Invoke the specified method using the specified string as console input.
 * Note that the calling class and types must be fully qualified.
 * 
 * @param inputString               Console input to be fed to the method
 * @param callingClassString        Class of the method to invoke
 * @param method                    Name of the method to invoke
 * @param typeStrings               Types the method takes (to distinguish it)
 * @param args                      Arguments to pass to the method
 * @return                          The console output captured
 */
public static void enterInput(String inputString,
        String callingClassString, String method, String[] typeStrings,
        Object... args) {
    try {
        Class<?>[] typeClasses = new Class<?>[typeStrings.length];
        for (int i = 0; i < typeStrings.length; i++) {
            typeClasses[i] = Class.forName(typeStrings[i]);
        }

        Method m = Class.forName(callingClassString).getMethod(method, typeClasses);
        InputStream origIn = System.in;
        System.setIn(new ByteArrayInputStream(inputString.getBytes()));
        m.invoke(null, args);
        System.setIn(origIn);
    } catch (Exception e) {
        throw new Error(e);
    }
}

/**
 * Invoke the specified method and return its console output as a string.
 * If inputString is not null, it is used as console input.
 * Note that the calling class and types must be fully qualified.
 * 
 * @param inputString               Console input to be fed to the method.
 *                                  If null, leaves this input stream alone
 * @param callingClassString        Class of the method to invoke
 * @param method                    Name of the method to invoke
 * @param typeStrings               Types the method takes (to distinguish it)
 * @param args                      Arguments to pass to the method
 * @return                          The console output captured
 */
public static String captureIO(String inputString, String callingClassString, String method, 
        String[] typeStrings, Object ... args) {
    String output = null;

    try {
        Class<?>[] typeClasses = new Class<?>[typeStrings.length];
        for (int i=0; i<typeStrings.length; i++) {
            typeClasses[i] = Class.forName(typeStrings[i]);
        }

        Method m = Class.forName(callingClassString).getMethod(method, typeClasses);
        InputStream origIn = System.in;
        PrintStream origOut = System.out;

        if (inputString != null)
            System.setIn(new ByteArrayInputStream(inputString.getBytes()));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        System.setOut(new PrintStream(baos));
        m.invoke(null, args);
        output = baos.toString();

        System.setOut(origOut);
        if (inputString != null)
            System.setIn(origIn);
    } catch (Exception e) {
        throw new Error(e);
    }
    return output;
}
share|improve this question
    
I think the code you try to test with these methods would also benefit from a review. If you have a method that prints some output that needs to be tested than pass in the System.out as a parameter. You can then pass in your byte output stream when you want to test. –  abuzittin gillifirca Jan 17 '13 at 12:13
add comment

2 Answers 2

This (and reflection in general) completely breaks refactoring, static code analysis and many more things and should therefore be avoided. (Imagine what happens if you find out a better naming for a function or you change the arguments.)

If you really need this behavior, you could use (I assume jUnit 4+) hooks before and after class/test case:

@BeforeClass
public static void setUpBeforeClass() throws Exception {
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
}

@Before
public void setUp() throws Exception {
}

@After
public void tearDown() throws Exception {
}

and do whatever is necessary.
Besides this, as already written, I would suggest to modify your methods, too. It could be a better approach to separate the view (prints) from model/controller (functions doing something).

share|improve this answer
    
+1 for recognizing that OP was trying to reinvent some functionality of JUnit. –  abuzittin gillifirca Jan 23 '13 at 12:16
add comment

In any case you should add the resetting of the in and out in the finally block, otherwise you will have problem in case an exception is thrown.


Nevertheless, did you ever thought of using Log4J or any other framework for logging? There you can easily define an Appender and decide where the log message will be written to.

In addition to that, your tests shouldn't check the logging. (Except you are testing the logging.) Try to create smaller method, where you can easily test the return values instead of the log output.

share|improve this answer
    
you should add the resetting of the in and out in the finally block, otherwise you will have problem in case an exception is thrown. There is a System.exit(1), as long as there is only one thread, it does not matter. But the statement is still true in the general case. –  tb- Jan 21 '13 at 18:40
    
You are right, I missed that. –  mnhg Jan 21 '13 at 20:01
    
Fixed those, thanks. –  pinecone Jan 22 '13 at 22:14
    
Throwing an Error directly is even worst than throwing a raw Exception. –  mnhg Jan 23 '13 at 4:36
    
By throwing the error, I mean to be doing the equivalent of System.exit(), but just for this one program. System.exit() kills the JVM. What should I be doing? –  pinecone Jan 27 '13 at 19:03
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.