Saturday, January 30, 2010

Testing servlets with Spring

So far I haven't had a need to test servlets within Spring framework environment. But the issue came up recently and I am going to share my experience with testing file upload servlet based on Apache FileUpload and Spring.

Let's start with a file upload servlet implementation. I will omit some unnecessary details and concentrate on two issues: get application context and retrieve/save file to disk.
public class FileUploadServlet extends HttpServlet { @Override public void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ApplicationContext appContext = WebApplicationContextUtils .getRequiredWebApplicationContext( getServletContext() ); // Get some beans here from application context ... DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload( factory ); try { Iterator< ? > iter = upload.parseRequest( request ).iterator(); while( iter.hasNext() ) { FileItem item = ( FileItem )iter.next(); if( !item.isFormField() ) { // store items here ... } } response.setStatus( HttpServletResponse.SC_OK ); } catch( Exception e ) { response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR ); } finally { response.flushBuffer(); } } }
Servlet is ready. Let's develop test case to verify it. There are basically three steps:
  • create mock request (and response)
  • create servlet instance and pass Spring application context to it
  • wrap file into request and call servlet's post()
The code fragment below shows how easy it could be done using Spring testing scaffolding (thanks Spring team again).
public class UploadServlerTestCase extends AbstractJUnit4SpringContextTests { private byte[] buffer; @Before public void setUp() throws Exception { // Load file content from resource final InputStream in = getClass().getResourceAsStream( "test.pdf" ); buffer = new byte[ in.available() ]; in.read( buffer ); in.close(); } @Test public void testFileUpload() { // create mock servlet config and pass Spring application context to it StaticWebApplicationContext ctx = new StaticWebApplicationContext(); ctx.setParent( applicationContext ); MockServletConfig sc = new MockServletConfig(); sc.getServletContext().setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx ); // create mock request (and response) MockHttpServletRequest request = new MockHttpServletRequest( "POST", "http://localhost/" ); MockHttpServletResponse response = new MockHttpServletResponse(); // wrap file into request final ByteArrayOutputStream out = new ByteArrayOutputStream(); try { out.write( String.format( "-----1234\r\n" + "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n" + "Content-Type: %s\r\n" + "\r\n", "textField", "test.pdf", "application/pdf" ).getBytes() ); out.write( buffer ); out.write( new String( "\r\n-----1234" ).getBytes() ); out.flush(); request.setContentType( "multipart/form-data; boundary=---1234" ); request.setContent( out.toByteArray() ); } finally { out.close(); } // create servlet instance and call post() FileUploadServlet servlet = new FileUploadServlet(); servlet.init( sc ); servlet.doPost( request, response ); // do some checks to ensure file has been stored ... } }
Test case is ready. Depending on your uploads management strategy (disk, database, Amazon S3, ...), test case should be extended to ensure that file has been stored by upload servlet at proper location.