15 August 2014

It's been quite awhile since I last posted but my wife and I had a kid in November and, despite my very vocal protestations, diapers trump TDD. Today's article is a short one, but I've found it useful ever since switching away from servlet definitions in web.xml to inline @WebServlet annotations. It's applicable to any type of annotations, I suppose, but for me the @WebServlet annotation comes up quite a bit in my day-to-day development.

First, here's our servlet class definition:

@WebServlet(
    name="Pleb Control", 
    description="This servlet is for keeping the plebs in check",
    loadOnStartup=1, 
    asyncSupported=true,
    urlPatterns={
        "/stupidplebs/subjugateBrutually", 
        "/stupidplebs/governFairly"
    }, 
    initParams = {
        @WebInitParam(name="enforcementMethod1", value="the stink eye"),
        @WebInitParam(name="enforcementMethod2", value="propaganda")
    }
)
public class StupidPlebServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        // do some stuff
    }
}

Back in the day when all your servlet and filter definitions were listed in web.xml, it was a bit cumbersome but quite possible to load web.xml in a test and verify settings using an XML parser. Inlining your servlet and filter configurations using @WebServlet and @WebFilter annotations doesn't work with this method but fortunately Java makes it pretty easy to iterate the declared annotations for a class and perform verifications. Here's a test in JUnit:

@Test
public void webServletAnnotationShouldBeConstructedWithExpectedConfigurationSettings() {
    // Given the first annotation (assumed to be WebServlet)
    WebServlet annotation = (WebServlet)StupidPlebServlet.class.getDeclaredAnnotations()[0];
    
    // Expect
    assertThat(annotation.name(), is("Pleb Control"));
    
    // And
    assertThat(annotation.description(), is(
            "This servlet is for keeping the plebs in check"));
    
    // And the servlet should be loaded when the container starts
    assertThat(annotation.loadOnStartup(), is(1));
    
    // And asynchronous processing should be supported
    assertThat(annotation.asyncSupported(), is(true));
    
    // And the servlet should be mapped to 2 url patterns
    assertThat(annotation.urlPatterns(), is(new String[]{
            "/stupidplebs/subjugateBrutually", 
            "/stupidplebs/governFairly"
    }));
    
    // And there should be 2 initialization parameters
    assertThat(annotation.initParams().length, is(2));
    assertThat(annotation.initParams()[0].name(), is("enforcementMethod1"));
    assertThat(annotation.initParams()[0].value(), is("the stink eye"));
    assertThat(annotation.initParams()[1].name(), is("enforcementMethod2"));
    assertThat(annotation.initParams()[1].value(), is("propaganda"));
}

Here's the same test in Spock:

def "@WebServlet annotation should be constructed with expected configuration settings"() {
    given: "groovy makes it easier to find the annotation we're interested"
    def annotation = StupidPlebServlet.class.declaredAnnotations.find { it ->
        it instanceof WebServlet
    }
    
    expect:
    annotation.name() == "Pleb Control"
    
    and:
    annotation.description() == 
            "This servlet is for keeping the plebs in check"
    
    and: "the servlet should be loaded when the container starts"
    annotation.loadOnStartup() == 1
    
    and: "asynchronous processing should be supported"
    annotation.asyncSupported()
    
    and: "the servlet should be mapped to 2 url patterns"
    annotation.urlPatterns() == [
        "/stupidplebs/subjugateBrutually", 
        "/stupidplebs/governFairly"
    ]
    
    and: "there should be 2 initialization parameters"
    annotation.initParams().length == 2
    annotation.initParams()[0].name() == "enforcementMethod1"
    annotation.initParams()[0].value() == "the stink eye"
    annotation.initParams()[1].name() == "enforcementMethod2"
    annotation.initParams()[1].value() == "propaganda"
}
This makes a very easy and convenient verification that a servlet will be loaded correctly by the container.