JAX-RS Application Testing Without a Web Server

This guide will walk you through building up a test framework for your JAX-RS application, without the need for a web server. It is the fastest way to ensure your JAX-RS application annotations and resource implementations are correct!

We are building on the Hello, World! sample from the Quick Start guide. This guide requires that you have JUnit3 (or 4) and EasyMock libraries on the project classpath.

  1. Create a peer directory of your src directory called "test" and designate it a source folder in your Eclipse project
  2. Develop your JUnit testcase class:
    1. Right-click the "test" source folder -> New -> Class
      As a best practice, you may wish to name your class with "Tests" at the end of your class name and use the same package name as the class being tested.

      For this example, we will be using JUnit3. You could just as simply use JUnit4.
      • Package: org.myorg.jaxrs.sample
      • Class: HelloWorldApplicationTests
      • Superclass: junit.framework.TestCase
    2. To simulate an inbound request to the servlet, we need several things in place:
      • A RESTServlet object
      • A ServletConfig object that has the initial params that the web.xml file would normally supply to the servlet container
      • An HttpServletRequest object that serves up the right request data
      We achieve this by using "mock" objects, configured in the JUnit3 setup() method. So far, our class should look like this:
      import static org.easymock.EasyMock.createNiceMock;
      import static org.easymock.EasyMock.expect;
      import static org.easymock.EasyMock.replay;
      import static org.easymock.EasyMock.verify;
      
      import java.io.ByteArrayInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.StringWriter;
      
      import javax.servlet.ServletConfig;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.ws.rs.HttpMethod;
      
      import junit.framework.TestCase;
      
      import com.ibm.ws.jaxrs.web.RESTServlet;
      
      public class HelloWorldApplicationTests extends TestCase {
      
          private HttpServletRequest request;
          private ServletConfig config;
          private RESTServlet servlet;
          
          @Override
          protected void setUp() throws Exception {
              super.setUp();
              // create mock objects - use Nice because we don't care how many times request
              // and response methods are called; we are testing our app, not JAX-RS
              request = createNiceMock(HttpServletRequest.class);
              config = createNiceMock(ServletConfig.class);
      
              // mock what you would normally set in the web.xml file
              expect(config.getInitParameter("javax.ws.rs.Application")).andReturn(
                      "org.myorg.jaxrs.sample.HelloWorldApplication");
              
              servlet = new RESTServlet();
          }
      
      }

      Good so far, but how do we capture the response from our servlet so we can inspect it and verify it? We need a mock HttpServletResponse object. More importantly, we need an HttpServletResponse object that writes to a locally accessible Writer so we can pull data from the Writer and verify it.
      import static org.easymock.EasyMock.createNiceMock;
      import static org.easymock.EasyMock.expect;
      import static org.easymock.EasyMock.replay;
      import static org.easymock.EasyMock.verify;
      
      import java.io.ByteArrayInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.StringWriter;
      
      import javax.servlet.ServletConfig;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.ws.rs.HttpMethod;
      
      import junit.framework.TestCase;
      
      import com.ibm.ws.jaxrs.web.RESTServlet;
      
      public class HelloWorldApplicationTests extends TestCase {
      
          private HttpServletRequest request;
          private HttpServletResponse response;
          private ServletConfig config;
          private RESTServlet servlet;
          private MockServletOutputStream sos;
          private StringWriter writer;  // we'll use this writer to get and inspect the servlet response body
          
          @Override
          protected void setUp() throws Exception {
              super.setUp();
              // create mock objects - use Nice because we don't care how many times request
              // and response methods are called; we are testing our app, not JAX-RS
              request = createNiceMock(HttpServletRequest.class);
              response = createNiceMock(HttpServletResponse.class);
              config = createNiceMock(ServletConfig.class);
      
              // mock what you would normally set in the web.xml file
              expect(config.getInitParameter("javax.ws.rs.Application")).andReturn(
                      "org.myorg.jaxrs.sample.HelloWorldApplication");
      
              // set up the mock response object so we can "intercept" and inspect the response message body
              writer = new StringWriter();
              sos = new MockServletOutputStream(writer);
              expect(response.getOutputStream()).andReturn(sos);
              
              servlet = new RESTServlet();
          }
          
          
          // we implement our own MockServletOutputStream to simplify the capture of the servlet response
          public class MockServletOutputStream extends javax.servlet.ServletOutputStream {
      
              private StringWriter sw;
      
              public MockServletOutputStream(StringWriter sw) {
                  this.sw = sw;
              }
      
              @Override
              public void write(int b) throws IOException {
                  sw.write(b);
              }
      
          }
      }

      Now we've got our response object configured to write to a local StringWriter object. We can grab that object at the end of each test method and inspect it for the data we expect. Let's write a test!
          public void testHTTPGet() throws Exception {
      
              // set up the mock request object
              expect(request.getMethod()).andReturn(HttpMethod.GET);
              // getRequestURL may be called multiple times in JAX-RS engine, hence
              // we need to configure expectations to allow it to be called many times.
              expect(request.getRequestURL()).andReturn(
                      new StringBuffer("/helloworld")).anyTimes();
              expect(request.getContextPath()).andReturn("");
              
              // turn on the EasyMock checking and behavior expectations
              replay(request);
              replay(response);
              replay(config);
      
              // initialize the servlet just like the servlet container would
              servlet.init(config);
              
              // execute the test
              servlet.doGet(request, response);
      
              // let EasyMock check that the behavior expectations are satisfied (in this example, the expectations are loosely defined)
              verify(request);
      
              // get the output stream buffer and verify the contents
              StringBuffer buffer = writer.getBuffer();
              assertEquals("Hello, World!", buffer.toString());
      
          }

      That's all there is to it! We've tested our full application, annotation processing and all, without ever starting up a web server.

      Let's extend our example a little bit. Let's say our Hello, World! application resource also supports a @POST method that simply echos what it receives:
      	@POST
      	public String helloWorldPost(String in) {
      	    return in;
      	}

      This presents a little bit of a challenge. We now need to send data via our HttpServletRequest object. No problem! We'll mock it up in much the same way as the HttpServletResponse object. Here is our new test, and our nested implementation class for ServletInputStream:
          public void testHTTPPost() throws Exception {
      
              // set up the mock request object
              expect(request.getMethod()).andReturn(HttpMethod.POST);  // note: POST instead of GET
              // getRequestURL may be called multiple times in JAX-RS engine, hence
              // we need to configure expectations to allow it to be called many times.
              expect(request.getRequestURL()).andReturn(
                      new StringBuffer("/helloworld")).anyTimes();
              expect(request.getContextPath()).andReturn("");
              // make sure the input stream carries the data we intend to be passed as the query
              expect(request.getInputStream()).andReturn(
                      new MockServletInputStream(new ByteArrayInputStream(
                              "Echo of Hello, World!".getBytes())));
      
              // turn on the EasyMock checking
              replay(request);
              replay(response);
              replay(config);
      
              servlet.init(config);
      
              // execute the test (note: doPost instead of doGet)
              servlet.doPost(request, response);
      
              // let EasyMock check that the behavior expectations are satisfied (in this example, the expectations are loosely defined)
              verify(request);
      
              // get the output stream buffer and verify the contents
              StringBuffer buffer = writer.getBuffer();
              assertEquals("Echo of Hello, World!", buffer.toString());
      
          }
      
          public class MockServletInputStream extends javax.servlet.ServletInputStream {
      
              private InputStream content;
      
              public MockServletInputStream(InputStream is) {
                  content = is;
              }
      
              @Override
              public int read() throws IOException {
                  return content.read();
              }
      
          }
  3. That's it! This is the fastest way to test that your annotations, code, and infrastructure are configured and implemented as you expect. You can execute this test just as you would any other JUnit test!

Notes