Tuesday, 28 May 2013

REST testing with embedded Grizzly

As mentioned in a previous post we use an embedded Grizzly server to help us when we test our RESTful services.  Earlier incarnations of this code involved setting up the server in each of the tests, which was cumbersome and not very DRY.

We tried a couple of alternatives before coming up with this solution that uses some of Groovy's support for fluent APIs.

Lets start by looking at the raw boilerplate required to get our embedded Grizzly server working for our PersonResource tests.
  
void startEmbeddedServer() {

     // Create test web application context.
    WebappContext webappContext = new WebappContext( "Test Context" )
    webappContext.addContextInitParameter( "contextClass", "org.springframework.web.context.support.XmlWebApplicationContext" )
    webappContext.addContextInitParameter( "contextConfigLocation", "classpath*:personService-web-applicationContext.xml" )
    webappContext.addListener( "org.springframework.web.context.ContextLoaderListener" )

    // Create a servlet registration for the web application in order to wire up Spring managed collaborators to Jersey resources.
    ServletRegistration servletRegistration = webappContext.addServlet( "jersey-servlet", "com.sun.jersey.spi.spring.container.servlet.SpringServlet" )

    // The LinkFilter is required for the HATEOAS annotations, logging filters for server logging.
    servletRegistration.setInitParameter( "com.sun.jersey.spi.container.ContainerResponseFilters", "com.sun.jersey.server.linking.LinkFilter;com.sun.jersey.api.container.filter.LoggingFilter" )
    servletRegistration.setInitParameter( "com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.LoggingFilter" )

    servletRegistration.setInitParameter( "com.sun.jersey.config.property.packages", "com.alkalinezoo.resource" )
    servletRegistration.setInitParameter( "com.sun.jersey.api.json.POJOMappingFeature", "true" )
    servletRegistration.addMapping( "/*" )

    grizzly = GrizzlyServerFactory.createHttpServer( "http://localhost:9998", new DefaultResourceConfig( PersonResource.class ) )

    webappContext.deploy( grizzly )
    
    grizzly.start()
  }

As you can see there is quite a lot of boilerplate code here.  We need a number of test specific details to be supplied, i.e. the package to scan for jersey annotations 'com.alkalinezoo.resource', the location of the Spring applicationConfig 'classpath*:personService-web-applicationContext.xml', the class under test 'PersonResource.class' etc.

There are also a couple of areas that we could improve, for example logging.  In the example above Grizzly logging is switched on via the use of the 'LoggingFilter'. It would be nice to be able to switch logging on / off in the test class.

It would be nice if we could specify this test specific configuration when we start the server and not have it embedded in the server config.

So, instead of adding all this config to our tests we have the following implementation:
  static final String BASE_URL = 'http://localhost:9998'
  static final String BASE_PACKAGE = 'com.alkalinezoo.resource'
  static final String SPRING_CONTEXT_LOCATION = 'classpath*:personService-web-applicationContext.xml'
  static final String CLASS_UNDER_TEST = 'com.alkalinezoo.resource.PersonResource'
  GrizzlyEmbeddedServer server = new GrizzlyEmbeddedServer()

@Before
  void setup( ) throws IOException {
    server.start { baseURL             BASE_URL
                   resourceBasePackage BASE_PACKAGE
                   loggingRequired     false
                   springContext       SPRING_CONTEXT_LOCATION
                   testClass           CLASS_UNDER_TEST
    }
  }
  @After
  void cleanup( ) {
    server.stop()
  }

The GrizzlyEmbeddedServer class defines a number of methods to set the test specific configuration elements of the class, for example:

  
private void springContext( String contextConfigLocation ) {
   this.contextConfigLocation = contextConfigLocation
}

private void resourceBasePackage( String basePackage ) {
   this.basePackage = basePackage
}

private void loggingRequired( Boolean serverLogging ) {
   this.serverLogging = serverLogging
}

We pass these to the start method as part of a closure that the method then applies to the class before calling the setUp() and start()  methods.  Here are the implementation details:

    
public void start( closure ) {
    this.with closure
    validateConfig()
    setUp()
    grizzly.start()
  }

The setUp() method then references these elements to set up the server appropriately, for example here's the  logging section:

    
if( serverLogging ) {
      servletRegistration.setInitParameter( "com.sun.jersey.spi.container.ContainerResponseFilters", "com.sun.jersey.server.linking.LinkFilter;com.sun.jersey.api.container.filter.LoggingFilter" )
      servletRegistration.setInitParameter( "com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.LoggingFilter" )
    }
    else {
      servletRegistration.setInitParameter( "com.sun.jersey.spi.container.ContainerResponseFilters", "com.sun.jersey.server.linking.LinkFilter" )
    }

Using this helper class makes our tests a lot easier to read and configure.

Monday, 20 May 2013

Testing Multiple HTTP Link Headers with REST Assured

We've recently had a requirement to validate the link headers coming back from a RESTful service as we are trying to be nice and HATEOAS-y because as we know REST APIs must be hypertext-driven.  We're using the REST assured testing framework, which is excellent.

In the following example we're calling a simple GET on a Person resource, the service returns JSON and two link headers to define resources that we can use for navigation.

We're using Jersey's 'declarative hyperlinking' support to define our resources using @Link annotations.  Here's our rather contrived example, this is written in Groovy so hence the square brackets in the annotations:

@Links( [
  @Link(
    value=@Ref( value='people/${instance.id}',
                style=Ref.Style.ABSOLUTE ),
    rel='me'
  ),
  @Link(
    value=@Ref( value='people/${instance.id}/abode/${instance.abode.id}',
                style=Ref.Style.ABSOLUTE,
                condition='${instance.abode != null}'),
    rel='house'
  )
] )
class Dude { 

  Long id
  String forename

  Abode abode
}

The problem we found was around having two headers with the same name i.e. two 'Links'.  We struggled to find any documentation on how to validate these links using REST assured.  After some messing about we came up with this solution, this is a Spock test written in Groovy:

  
def "Fetch a person with rest-assured check link headers"( ) {
   setup:
      grizzly.start()

    when:
      def response = 
        given()
          .header( HttpHeaders.ACCEPT, ContentType.JSON.toString() ).
        expect()
            .statusCode( HttpStatus.SC_OK ).
        when()
            .get( "${BASE_URI}/people/{id}", '10' )
    
      def linkHeaders = extractLinkHeaders( response.headers.getValues( 'Link' ) )

    then:
      linkHeaders.me == 'http://localhost:9998/people/99'
    and:
      linkHeaders.house == 'http://localhost:9998/people/99/abode/98'

    cleanup:
      grizzly.stop()
  }

Other things to note about this test.  We're using an embedded Grizzly server to test the service. The link header values are returned by the call to  response.headers.getValues( 'Link' ).  The extractLinkHeaders() method converts the 'raw' link strings into a Groovy map which makes the testing of the link rel attributes a lot neater.