The mock output stream in Listing Three is simpler. The write() method just buffers the
characters as they're written, and we can call getOutput() to get that string once the Servlet's done with it.
Lisitng Three
package com.ecmhp.mockobjects;
import java.io.*;
import java.util.*;
import javax.servlet.ServletOutputStream;
public class MockServletOutputStream extends ServletOutputStream
{
private StringBuilder output = new StringBuilder();
private LinkedList<Character> charactersReceived = new LinkedList<Character>();
private boolean returnedEOF = false;
@Override public void write(int b) throws IOException
{
if( returnedEOF == true ) // clear out the previous buffer-full of characters
{ returnedEOF = false;
output.setLength(0);
charactersReceived.clear();
}
output.append( (char)b );
charactersReceived.addLast( (char)b );
}
/** Return all of the output that was sent to this stream. This method
* interacts with read() in that the output buffer is by writing
* a character immediately after read() returned -1.
*/
public String getOutput()
{ return output.toString();
}
/** Read one character from the stream. Return -1 if there's nothing to read.
* Note that if new output is written to the stream after read() has returned
* -1, that new output will be returned until it's exhausted (in which case
* -1 is returned, again). close() has no impact on this behavior.
*/
public int read()
{ if( charactersReceived.size() <= 0 )
{ returnedEOF = true;
return -1;
}
return charactersReceived.removeFirst();
}
}
My template for a typical servlet test that uses these classes is shown in Listing Four.
The doTest() method first manufactures mocks of the HttpServletRequest and HttpServletResponse objects that
we'll pass to the servlet. It then creates instances of our mock input and output streams, and finally arranges for the
mock request and response objects to return our versions of the input or output streams as required. For example,
when( request.getInputStream() ).thenReturn( in );
says that when the servlet calls request.getInputStream(), the mock will return in, an instance of the MockServletInputStream
class we just looked at.
We run the test by calling testDoPost(), passing it our mocks, then we verify that things work — first by checking that
sendError() was never called on the response object, and then by checking that the string returned from the
request was written verbatim into the response. Of course, your servlet will hardly every do exactly that, but this is
template code that I'll modify to actually verify that the output is what I expect it to be.
The second method in Listing Four creates 20 threads, each of which calls the doTest() method we just looked at.
The threads are queued up at a CountDownLatch, which works like the starting gate at a racetrack. As the loop creates the threads, it
calls countDown() for each thread (which will cause the thread to queue up at the gate). When the count counts down to zero, all the
waiting threads are released simultaneously, so they all run more or less in parallel.
My hope is that this test will simulate a heavy enough load on the server that it will uncover any threading-related bugs.
Listing Four
package com.ecmhp.tests;
import static org.junit.Assert.*;
import java.io.*;
import java.util.concurrent.CountDownLatch;
import javax.jms.JMSException;
import javax.naming.NamingException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.junit.*;
import org.mockito.Mockito;
public class ServletTest extends Mockito
{
@Test
public void doTest() throws Exception
{
String requestString = "Request Contents";
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
MockServletInputStream in = new MockServletInputStream( requestString );
MockServletOutputStream out = new MockServletOutputStream();
in.setDelay(0); // no delay on read to stress the system as much as possible.
// Use mocks for the input and output streams fetched from the (mock) request and response objects
// You can also mock other request/response methods (to supply an expected URL, for example).
//
when( request.getInputStream() ).thenReturn( in );
when( response.getOutputStream() ).thenReturn( out );
//======================================================================
// Run the test
//======================================================================
testObject.testDoPost( request, response ); // test using the mock request/response objects.
//======================================================================
// TODO: This statement, when running on a thread, does
// not cause Eclipse to indicate a JUnit error. It does
// report the problem to the console, however.
//
verify(response, never()).sendError( anyInt(), anyString() ); // no error responses sent to client.
assertEquals( request, out.getOutput() );
}
@Test
public void testUsingMultipleThreads() throws Exception
{
Thread.sleep( 2000L ); // wait a couple seconds to make sure everything's stopped
final int numThreads = 20;
final CountDownLatch done = new CountDownLatch( numThreads );
for( int i = 0; i < numThreads; ++i )
{
final int threadID = i; // must be final to be accessible from within inner class
new Thread()
{ @Override public void run()
{
System.err.println("================ Thread " + threadID + " started ================ ");
Exception cause;
try
{
doTest();
System.err.println("================ Thread " + threadID + " terminating normally ================ ");
return;
}
catch (IOException e) {cause = e;}
catch (ServletException e) {cause = e;}
catch (Exception e) {cause = e;}
finally
{ done.countDown();
}
System.err.println("Exception in thread:");
cause.printStackTrace();
fail();
}
}.start();
}
done.await();
}
}


