See also: JavaUnit ---- ''Input Requested: How can we make this better? Maybe factor into separate pages? -- EricHerman'' Here are some handy tactics that can be used for testing in JavaLanguage. ----- '''Swap ''System.out'' with a testable alternative''' How do you test a program like Hello World: public class HelloWorld { public static void main( String [] args) throws Exception { System.out.println("Hello, World."); } } This is one option: import junit.framework.*; import junit.runner.BaseTestR''''''unner; import java.io.*; public class HelloWorldT''''''est extends TestCase{ public HelloWorldT''''''est(String name) { super(name); } public void testMain() throws Exception { ByteArrayOutputS''''''tream testOut = new ByteArrayOutputS''''''tream(); Print''''''Stream testOutPrintStream = new Print''''''Stream(testOut); System.setOut(testOutPrintStream); Hello''''World.main(new String[0]); /* Be careful of the Line''''Separator with println */ String newLine = System.getProperty("line.separator"); if (newLine == null) newLine = "\n"; String expected = "Hello, World." + newLine; assertEquals("First Assert", expected, testOut.toString()); /* Must reset() between reads! */ assertEquals("Test Same", expected, testOut.toString()); testOut.reset(); assertEquals("Test Empty", "", testOut.toString()); demo.main(new String[0]); assertEquals( "again", expected, testOut.toString()); } } ------ '''Swap ''System.err'' as well.''' The ''System.err'' may swapped out just like the ''System.out''. To test this class: public class PrintStackT''''race { public static void main( String [] args) throws Exception { Exception e = new Exception("Print Me"); e.printStackTrace(); } } One option would be: import junit.framework.*; import junit.runner.BaseTestR''''unner; import java.io.*; public class PrintStackTraceT''''est extends TestCase{ public PrintStackTraceT''''est(String name) { super(name); } public void testMain() throws Exception { ByteArrayOutputS''''tream testErr = new ByteArrayOutputS''''tream(); Print''''Stream testErrPrintStream = new Print''''Stream(testErr); System.setErr(testErrPrintStream); PrintStackT''''race.main(new String[0]); assertTrue("1", testErr.toString().startsWith("java.lang.Exception: Print Me")); } } ------ '''Swapping ''System.in'' ''' Swapping ''System.in'' can be tricky. Here is a basic example: import junit.framework.*; import junit.runner.BaseTestR''''''unner; import java.io.*; public class SwapInT''''''est extends TestCase{ public SwapInT''''''est(String name) { super(name); } public void testReplaceSystemIn() throws Exception { PipedInputS''''''tream testInPipe = new PipedInputS''''''tream(); PipedOutputS''''''tream testOutPipe = new PipedOutputS''''''tream(); testInPipe.connect(testOutPipe); System.setIn(testInPipe); InputStreamR''''''eader isr = new InputStreamR''''''eader(System.in); Buffered''''''Reader reader = new Buffered''''''Reader(isr); String expected = "This is a Test."; String withline = expected + "\n"; testOutPipe.write(withline.getBytes(), 0, withline.length()); assertEquals("Test One", expected, reader.readLine()); } } Of course, Reading and Writing from the same thread is risky: we open ourselves up to deadlock problems. If our tests fail, we want them to fail correctly, not just hang in an infinite loop. For a more robust test, we'll need to look into threads. Luckily, (unlike keyboard and console I/O which java makes hard) threading in java is fairly easy. ---- '''How to test Console Keyboard input?''' Even with easy threading options, the problem is still a hard one. I'm still working on this in my own head, and I welcome any input. In the prior example, we were careful to add a newline ('\n') character before performing a call to the readline() method. Without that newline, readline will never return. This isn't so bad in a test where we can see at a glance that we are providing usable data and using acceptably it. In most cases, the call to read input will not be in the test but rather in the class that the test is testing. How do we test a class like this: import java.io.*; public class Hello''''''You { public static void main( String [] args) throws Exception { InputStreamR''''''eader isr = new InputStreamR''''''eader(System.in); Buffered''''''Reader inStream = new Buffered''''''Reader(isr); System.out.print("What is your name? "); String name = inStream.readLine(); System.out.println("Hello, " + name + "."); } Sure, we can only write tests that stock ''System.in'' with newline characters at the end, but that doesn't seem like a very good solution. Console keyboard input is especially hard to write tests for because of the deadlock issues. Should we spawn a new thread in our test to supply fake user input Should we try to extend our testing framework to kill off a test if it takes more than "x" amount of time to complete? MartinFowler has suggested that the most important criterion for software design is testability. Perhaps, we get the same functionality out of the Hello''''''You class with a better design that won't hang our test? Perhaps our program under test could spin off a separate thread to "listen" for input, not blocking the main thread of execution. What are your thoughts? -- EricHerman ---- See also: http://showell.westhost.com/cgi-bin/XpSeattleWiki.pl?InteractiveTestingInJava '' (BrokenLink 2006-01-07)''