Reputation: 11909
I would like to know what would be the best way to do unit testing of a servlet.
Testing internal methods is not a problem as long as they don't refer to the servlet context, but what about testing the doGet/doPost methods as well as the internal method that refer to the context or make use of session parameters?
Is there a way to do this simply using classical tools such as JUnit, or preferrably TestNG? Did I need to embed a tomcat server or something like that?
Upvotes: 56
Views: 56024
Reputation: 184
This Question has a solution proposing Mockito How to test my servlet using JUnit This limits the task to simple unit testing, without setting up any server-like environment.
Upvotes: 0
Reputation: 49
Updated Feb 2018: OpenBrace Limited has closed down, and its ObMimic product is no longer supported.
Another solution is to use my ObMimic library, which is specifically designed for unit testing of servlets. It provides complete plain-Java implementations of all the Servlet API classes, and you can configure and inspect these as necessary for your tests.
You can indeed use it to directly call doGet/doPost methods from JUnit or TestNG tests, and to test any internal methods even if they refer to the ServletContext or use session parameters (or any other Servlet API features).
This doesn't need an external or embedded container, doesn't limit you to broader HTTP-based "integration" tests, and unlike general-purpose mocks it has the full Servlet API behaviour "baked in", so your tests can be "state"-based rather than "interaction"-based (e.g. your tests don't have to rely on the precise sequence of Servlet API calls made by your code, nor on your own expectations of how the Servlet API will respond to each call).
There's a simple example in my answer to How to test my servlet using JUnit. For full details and a free download see the ObMimic website.
Upvotes: 0
Reputation: 1113
I looked at the posted answers and thought that I would post a more complete solution that actually demonstrates how to do the testing using embedded GlassFish and its Apache Maven plugin.
I wrote the complete process up on my blog Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x and placed the complete project for download on Bitbucket here: image-servlet
I was looking at another post on an image servlet for JSP/JSF tags just before I saw this question. So I combined the solution I used from the other post with a complete unit tested version for this post.
Apache Maven has a well defined lifecycle that includes test
. I will use this along with another lifecycle called integration-test
to implement my solution.
integration-test
as part of the executions of the surefire-pluginintegration-test
lifecycle.Add this plugin as part of the <build>
.
<plugin>
<groupId>org.glassfish</groupId>
<artifactId>maven-embedded-glassfish-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<!-- This sets the path to use the war file we have built in the target directory -->
<app>target/${project.build.finalName}</app>
<port>8080</port>
<!-- This sets the context root, e.g. http://localhost:8080/test/ -->
<contextRoot>test</contextRoot>
<!-- This deletes the temporary files during GlassFish shutdown. -->
<autoDelete>true</autoDelete>
</configuration>
<executions>
<execution>
<id>start</id>
<!-- We implement the integration testing by setting up our GlassFish instance to start and deploy our application. -->
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
<goal>deploy</goal>
</goals>
</execution>
<execution>
<id>stop</id>
<!-- After integration testing we undeploy the application and shutdown GlassFish gracefully. -->
<phase>post-integration-test</phase>
<goals>
<goal>undeploy</goal>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
Add/modify the plugin as part of the <build>
.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<!-- We are skipping the default test lifecycle and will test later during integration-test -->
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<!-- During the integration test we will execute surefire:test -->
<goal>test</goal>
</goals>
<configuration>
<!-- This enables the tests which were disabled previously. -->
<skip>false</skip>
</configuration>
</execution>
</executions>
</plugin>
Add integration tests like the example below.
@Test
public void badRequest() throws IOException {
webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
webClient.getOptions().setPrintContentOnFailingStatusCode(false);
final HtmlPage page = webClient.getPage("http://localhost:8080/test/images/");
final WebResponse response = page.getWebResponse();
assertEquals(400, response.getStatusCode());
assertEquals("An image name is required.", response.getStatusMessage());
webClient.getOptions().setThrowExceptionOnFailingStatusCode(true);
webClient.getOptions().setPrintContentOnFailingStatusCode(true);
webClient.closeAllWindows();
}
I wrote the complete process up on my blog Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x and placed the complete project for download on Bitbucket here: image-servlet
If you have any questions, please leave a comment. I think that this is one complete example for you to use as the basis of any testing you are planning for servlets.
Upvotes: 11
Reputation: 311
This implementation of a JUnit test for servlet doPost() method relies only on the Mockito library for mocking up instances of HttpRequest
, HttpResponse
, HttpSession
, ServletResponse
and RequestDispatcher
. Replace parameter keys and JavaBean instance with those that correspond to values referenced in the associated JSP file from which doPost() is called.
Mockito Maven dependency:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
</dependency>
JUnit test:
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
* Unit tests for the {@code StockSearchServlet} class.
* @author Bob Basmaji
*/
public class StockSearchServletTest extends HttpServlet {
// private fields of this class
private static HttpServletRequest request;
private static HttpServletResponse response;
private static StockSearchServlet servlet;
private static final String SYMBOL_PARAMETER_KEY = "symbol";
private static final String STARTRANGE_PARAMETER_KEY = "startRange";
private static final String ENDRANGE_PARAMETER_KEY = "endRange";
private static final String INTERVAL_PARAMETER_KEY = "interval";
private static final String SERVICETYPE_PARAMETER_KEY = "serviceType";
/**
* Sets up the logic common to each test in this class
*/
@Before
public final void setUp() {
request = mock(HttpServletRequest.class);
response = mock(HttpServletResponse.class);
when(request.getParameter("symbol"))
.thenReturn("AAPL");
when(request.getParameter("startRange"))
.thenReturn("2016-04-23 00:00:00");
when(request.getParameter("endRange"))
.thenReturn("2016-07-23 00:00:00");
when(request.getParameter("interval"))
.thenReturn("DAY");
when(request.getParameter("serviceType"))
.thenReturn("WEB");
String symbol = request.getParameter(SYMBOL_PARAMETER_KEY);
String startRange = request.getParameter(STARTRANGE_PARAMETER_KEY);
String endRange = request.getParameter(ENDRANGE_PARAMETER_KEY);
String interval = request.getParameter(INTERVAL_PARAMETER_KEY);
String serviceType = request.getParameter(SERVICETYPE_PARAMETER_KEY);
HttpSession session = mock(HttpSession.class);
when(request.getSession()).thenReturn(session);
final ServletContext servletContext = mock(ServletContext.class);
RequestDispatcher dispatcher = mock(RequestDispatcher.class);
when(servletContext.getRequestDispatcher("/stocksearchResults.jsp")).thenReturn(dispatcher);
servlet = new StockSearchServlet() {
public ServletContext getServletContext() {
return servletContext; // return the mock
}
};
StockSearchBean search = new StockSearchBean(symbol, startRange, endRange, interval);
try {
switch (serviceType) {
case ("BASIC"):
search.processData(ServiceType.BASIC);
break;
case ("DATABASE"):
search.processData(ServiceType.DATABASE);
break;
case ("WEB"):
search.processData(ServiceType.WEB);
break;
default:
search.processData(ServiceType.WEB);
}
} catch (StockServiceException e) {
throw new RuntimeException(e.getMessage());
}
session.setAttribute("search", search);
}
/**
* Verifies that the doPost method throws an exception when passed null arguments
* @throws ServletException
* @throws IOException
*/
@Test(expected = NullPointerException.class)
public final void testDoPostPositive() throws ServletException, IOException {
servlet.doPost(null, null);
}
/**
* Verifies that the doPost method runs without exception
* @throws ServletException
* @throws IOException
*/
@Test
public final void testDoPostNegative() throws ServletException, IOException {
boolean throwsException = false;
try {
servlet.doPost(request, response);
} catch (Exception e) {
throwsException = true;
}
assertFalse("doPost throws an exception", throwsException);
}
}
Upvotes: 3
Reputation: 11264
Most of the time I test Servlets and JSP's via 'Integration Tests' rather than pure Unit Tests. There are a large number of add-ons for JUnit/TestNG available including:
This is a JWebUnit test for a simple Order Processing Servlet which processes input from the form 'orderEntry.html'. It expects a customer id, a customer name and one or more order items:
public class OrdersPageTest {
private static final String WEBSITE_URL = "http://localhost:8080/demo1";
@Before
public void start() {
webTester = new WebTester();
webTester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT);
webTester.getTestContext().setBaseUrl(WEBSITE_URL);
}
@Test
public void sanity() throws Exception {
webTester.beginAt("/orderEntry.html");
webTester.assertTitleEquals("Order Entry Form");
}
@Test
public void idIsRequired() throws Exception {
webTester.beginAt("/orderEntry.html");
webTester.submit();
webTester.assertTextPresent("ID Missing!");
}
@Test
public void nameIsRequired() throws Exception {
webTester.beginAt("/orderEntry.html");
webTester.setTextField("id","AB12");
webTester.submit();
webTester.assertTextPresent("Name Missing!");
}
@Test
public void validOrderSucceeds() throws Exception {
webTester.beginAt("/orderEntry.html");
webTester.setTextField("id","AB12");
webTester.setTextField("name","Joe Bloggs");
//fill in order line one
webTester.setTextField("lineOneItemNumber", "AA");
webTester.setTextField("lineOneQuantity", "12");
webTester.setTextField("lineOneUnitPrice", "3.4");
//fill in order line two
webTester.setTextField("lineTwoItemNumber", "BB");
webTester.setTextField("lineTwoQuantity", "14");
webTester.setTextField("lineTwoUnitPrice", "5.6");
webTester.submit();
webTester.assertTextPresent("Total: 119.20");
}
private WebTester webTester;
}
Upvotes: 46
Reputation: 3308
Mockrunner (http://mockrunner.sourceforge.net/index.html) can do this. It provides a mock J2EE container that can be used to test Servlets. It can also be used to unit test other server-side code like EJBs, JDBC, JMS, Struts. I've only used the JDBC and EJB capabilities myself.
Upvotes: 6
Reputation: 14587
Are you calling the doPost and doGet methods manually in the unit tests? If so you can override the HttpServletRequest methods to provide mock objects.
myServlet.doGet(new HttpServletRequestWrapper() {
public HttpSession getSession() {
return mockSession;
}
...
}
The HttpServletRequestWrapper is a convenience Java class. I suggest you to create a utility method in your unit tests to create the mock http requests:
public void testSomething() {
myServlet.doGet(createMockRequest(), createMockResponse());
}
protected HttpServletRequest createMockRequest() {
HttpServletRequest request = new HttpServletRequestWrapper() {
//overrided methods
}
}
It's even better to put the mock creation methods in a base servlet superclass and make all servlets unit tests to extend it.
Upvotes: 7
Reputation: 17344
Try HttpUnit, although you are likely to end up writing automated tests that are more 'integration tests' (of a module) than 'unit tests' (of a single class).
Upvotes: 13