Testing Servlets with Mockito
When I write Java-projects that produce web pages, I test on several levels. First, I use Jetty and WebDriver to test the result via http. (WebDriver even lets me test JavaScript!)
But for speed and agility, unit-level tests beat even the fastest integration level tests. Unit tests run much much faster, and lets me pinpoint problems much more accurately. As a general rule: If you introduce a bug, you want a unit test to be the one that sees it first.
For web applications in Java, I’ve found the following pattern useful when testing classes that deal with HttpServletRequest
and HttpServletResponse
:
@Test
public void shouldDecodeSearchParameters() throws IOException {
HttpServletRequest req = mock(HttpServletRequest.class);
HttpServletResponse resp = mock(HttpServletResponse.class);
Repository repository = mock(Repository.class);
PersonController personController =
new PersonController(req, resp, repository);
given(req.getParameter("firstName")).willReturn("johannes");
given(req.getParameter("lastName")).willReturn("brodwall");
given(req.getParameter("includePaidMembers"))
.willReturn("true");
given(req.getParameter("includeUnpaidMembers"))
.willReturn("false");
when(resp.getWriter())
.thenReturn(new PrintWriter(new StringWriter()));
personController.find();
ArgumentCaptor< PersonSpecification> specificationCapture =
ArgumentCaptor.forClass(PersonSpecification.class);
verify(repository).find(specificationCapture.capture());
PersonSpecification personSpec =
specificationCapture.getValue();
assertThat(personSpec.getFirstName()).isEqualTo("johannes");
assertThat(personSpec.getLastName()).isEqualTo("brodwall");
assertThat(personSpec.isIncludingPaidMembers())
.isEqualTo(true);
assertThat(personSpec.isIncludingUnpaidMembers())
.isEqualTo(false);
}
1: This example uses Mockito to specify what calls to HttpServletRequest
should return. A really nice thing about Mockito is that you only have to specify what you care about. If, for example, PersonController
ends up calling req.getParameter("nonexistant")
, Mockito will simply return null. If PersonContoller
asks for “lastName” several times, Mockito will keep returning “brodwall”.
2: Mockito doesn’t verify that a set of methods were called unless you explicitly ask it to. PersonController
happens to call several methods on HttpServletResponse
that we don’t even mention in the test.
3: Mockito lets you look at what arguments were used to call a mocked-out method after the method was called by using an ArgumentCaptor
.
One caveat, though: In the case of HttpServletRequest
, the production code can decide to read the request parameters in several different ways: getParameter
, getParameterValues
or getParameterMap
. Mockito will naturally not have any idea of the relationship between these methods, so your test will be sensitive to this sort of implementation change.
After working with EasyMock and JMock for a while, I seriously lost faith in mocking. Mockito has restored my faith again!
Happy testing!
Comments:
Johannes Brodwall - Jul 16, 2012
Hi, Karhan. The code is taken from my Java-EE-spike-kata, which is here:Â https://github.com/jhannes/java-ee-spike-kata
[Alok Chandra] - Feb 27, 2012
nice example.
[Karhan] - Jul 16, 2012
Would be really nice if you would put the whole code for seeing which libs to import