Thursday, 9 May 2013

Spring MVC and the HATEOAS constraint

HATEOAS is a REST architecture principle where hypermedia is used to change application state. To change state, the returned resource representation contains links thereby 'constraining' the client on what steps to take next.

The Spring-HATEOAS project aims to assist those writing Spring MVC code in the creation of such links and the assembly of resources returned to the clients.

The example below will cover a simple scenario showing how links are created and returned for the resource, Bet. Each operation on the resource is described below:

  • createBet - this POST operation will create a Bet.
  • updateBet - this PUT operation will update the Bet.
  • getBet - this GET operation will retrieve a Bet.
  • cancelBet - this DELETE operation will cancel the Bet.

@Controller
@RequestMapping("/bets")
public class BetController {

 private BetService betService;
 private BetResourceAssembler betResourceAssembler;

 public BetController(BetService betService,
   BetResourceAssembler betResourceAssembler) {
  this.betService = betService;
  this.betResourceAssembler = betResourceAssembler;
 }

 @RequestMapping(method = RequestMethod.POST)
 ResponseEntity<BetResource> createBet(@RequestBody Bet body) {
  Bet bet = betService.createBet(body.getMarketId(),
    body.getSelectionId(), body.getPrice(), body.getStake(),
    body.getType());
  BetResource resource = betResourceAssembler.toResource(bet);
  return new ResponseEntity<BetResource>(resource, HttpStatus.CREATED);
 }

 @RequestMapping(method = RequestMethod.PUT, value = "/{betId}")
 ResponseEntity<BetResource> updateBet(@PathVariable Long betId,
   @RequestBody Bet body) throws BetNotFoundException, BetNotUnmatchedException {
  Bet bet = betService.updateBet(betId, body);
  BetResource resource = betResourceAssembler.toResource(bet);
  return new ResponseEntity<BetResource>(resource, HttpStatus.OK);
 }

 @RequestMapping(method = RequestMethod.GET, value = "/{betId}")
 ResponseEntity<BetResource> getBet(@PathVariable Long betId) throws BetNotFoundException {
  Bet bet = betService.getBet(betId);
  BetResource resource = betResourceAssembler.toResource(bet);
  if (bet.getStatus() == BetStatus.UNMATCHED) {
   resource.add(linkTo(BetController.class).slash(bet.getId()).withRel("cancel"));
  }
  return new ResponseEntity<BetResource>(resource, HttpStatus.OK);
 }

 @RequestMapping(method = RequestMethod.GET)
 ResponseEntity<List<BetResource>> getBets() {
  List<Bet> betList = betService.getAllBets();
  List<BetResource> resourceList = betResourceAssembler.toResources(betList);
  return new ResponseEntity<List<BetResource>>(resourceList, HttpStatus.OK);
 }

 @RequestMapping(method = RequestMethod.DELETE, value = "/{betId}")
 ResponseEntity<BetResource> cancelBet(@PathVariable Long betId) {
  Bet bet = betService.cancelBet(betId);
  BetResource resource = betResourceAssembler.toResource(bet);
  return new ResponseEntity<BetResource>(resource, HttpStatus.OK);
 }

 @ExceptionHandler
 ResponseEntity handleExceptions(Exception ex) {
  ResponseEntity responseEntity = null;
  if (ex instanceof BetNotFoundException) {
   responseEntity = new ResponseEntity(HttpStatus.NOT_FOUND);
  } else if (ex instanceof BetNotUnmatchedException) {
   responseEntity = new ResponseEntity(HttpStatus.CONFLICT);
  } else {
   responseEntity = new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
  }
  return responseEntity;
 }
 
}

 
All the operations will create a BetResource for returning to the client. This is done by calling toResource on the BetResourceAssembler class:

public class BetResourceAssembler extends ResourceAssemblerSupport<Bet, BetResource> {

 public BetResourceAssembler() {
  super(BetController.class, BetResource.class);
 }

 public BetResource toResource(Bet bet) {
  BetResource resource = instantiateResource(bet);
  resource.bet = bet;
        resource.add(linkTo(BetController.class).slash(bet.getId()).withSelfRel());
  return resource;
 }

}


This class extends ResourceAssemblerSupport which requires the implementation of a toResource method as it implements the ResourceAssembler interface. This is where the mapping between Bet and BetResource is done. In this case, BetResource is just a wrapper for Bet so it is simply a case of setting the bet attribute. The instantiateResource method will return a BetResource without any links so links can be added at this point if required. In this example a link to self is added. An alternative approach would be to use createResourceWithId which will return a BetResource with the self link. 
 
public class BetResource extends ResourceSupport {
 
 public Bet bet;
 
}

 
Also in this example, links are added to the BetResource within the BetController class to ensure the application of the HATEOAS constraint. If the REST service receives a GET request then a check is made on the status of the Bet. If the Bet is UNMATCHED, then a link to cancel the Bet can be added to the BetResource. This is done in similar fashion to the self link but with the relationship attribute name of cancel.
 
An alternative approach to this is to build a link to a method as opposed to constructing a URI. 
 
resource.add(linkTo(methodOn(BetController.class).cancelBet(betId))
.withRel("cancel")); 
 
The methodOn would create a proxy of the BetController class and as a result the return type of the cancelBet method would have to be capable of proxying. Therefore in this example the return type of cancelBet method would be HttpEntity<Bet> and not ResponseEntity<Bet>. If the latter, then the likely exception from the server would be:
 
[org.springframework.http.ResponseEntitycom.city81.hateoas.rest.BetResource> com.city81.hateoas.controller.BetController.getBet(java.lang.Long) throws com.city81.hateoas.BetNotFoundException]:org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class org.springframework.http.ResponseEntity]: common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
 
Back to the GET request, and the returned JSON for requesting a Bet resource which has a status of UNMATCHED is shown below:

{
 "links":[
     {"rel":"self","href":http://localhost:8080/hateoas-1-SNAPSHOT/bets/0},
     {"rel":"cancel","href":http://localhost:8080/hateoas-1-SNAPSHOT/bets/0}
],
 "bet":{"id":0,"marketId":1,"selectionId":22,"price":4.0,"stake":2.0,"type":"BACK","status":"UNMATCHED"}
}

The client can therefore use the self link for retrieving and updating the Bet, and also the cancel link to effectively delete it.

This post describes just some of the functionality of the Spring-HATEOAS project which is evolving all the time. For an up to date and more detailed explanation, visit the GitHub pages.
 
 
 
 

Thursday, 4 April 2013

Mocking Static Methods in Groovy

Using Groovy to test not only other Groovy classes but also Java classes is an increasing popular approach given frameworks like Spock which facilitate Test Driven and Behaviour Driven Development.

A common problem when testing though is having to deal with legacy code and more often than not having to mock static methods calls. When writing tests in Groovy, the approach to mocking static calls will depend on the type of class you're testing and what type of class has the static method in.

If the Groovy class you're testing makes calls a static method on another Groovy class, then you could use the ExpandoMetaClass which allows you to dynamically add methods, constructors, properties and static methods. The below Account Groovy class has a static method called getType(). The Customer Groovy class you're trying to test calls the getType() static method within it's getAccountType() method:


class Customer {

 String getAccountType() {
  return Account.getType();
 }
 
}

class Account {

 static String getType() {
  return "Personal";
 }

}

class CustomerTest extends GroovyTestCase {
 
 void testGetAccountType() {
  
  Customer cust = new Customer()
  assert cust.getAccountType() == "Personal"

  Account.metaClass.static.getType = {return "Business"}
  
  assert cust.getAccountType() == "Business"
    
 }

}

The CustomerTest Groovy class shows how the getType static method can be overridden using the metaClass property. This isn't strictly mocking, more dynamically changing a class. An alternative approach is to use Spock's GroovyMock to achieve a similar result. (A more detailed description of Spock's mocking and stubbing features can be found here:)


import spock.lang.*

class CustomerTest extends Specification {
 
 def "get account type"() {
   
  setup: 
   GroovyMock(Account, global: true)
   Account.getType() >> "Business"
   Customer cust = new Customer()
  
  when: "obtaining a customer's account type"
   def type = cust.getAccountType()

  then: "the type will be a Business account"
   type == "Business"
    
 }

}

If the Account class is a Java not Groovy class then we can still mock it out using the above methods. The problem with mocking Java static methods though, when using Groovy for testing, is when they are called from Java classes and these calling classes are the ones you are trying to test.

For example, if we now have a Customer Java class and also an Account Java class, and a CustomerTest Groovy class, the ExpandoMetaClass and GroovyMock approaches will not work globally but will work locally. The below CustomerTest will pass.


public class Customer {

 public String getAccountType() {
  return Account.getType();
 }
 
}

public class Account {

 public static String getType() {
  return "Personal";
 }

}
class CustomerTest extends GroovyTestCase {
 
 void testGetAccountType() {
  
  Customer cust = new Customer()
  assert cust.getAccountType() == "Personal"

  Account.metaClass.static.getType = {return "Business"}
  
  assert Account.getType() == "Business"
  assert cust.getAccountType() == "Personal"
    
 }

}

The way around this is to use a mocking framework like Powermock or JMockit. The below amended CustomerTest Groovy class shows how Powermock can be integrated into a GroovyTestCase:


import groovy.mock.interceptor.*
import org.powermock.api.mockito.PowerMockito;
import static org.mockito.Mockito.*;
import org.powermock.core.classloader.annotations.PrepareForTest
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner

@RunWith( PowerMockRunner.class )
@PrepareForTest(Account.class)
class CustomerTest extends GroovyTestCase {
 
 public void testGetAccountType() { 
  
  Customer cust = new Customer()
  assert cust.getAccountType() == "Personal"

  PowerMockito.mockStatic(Account.class);
  when(Account.getType()).thenReturn("Business");
    
  assert cust.getAccountType() == "Business"
 
 }

}
Using Mockito's when to mock the return value of the static getType() call enables "Business" to be returned instead of "Personal". This example shows how when writing Groovy tests to test Java classes, using a framework like Powermock can fill the void of mocking Java static methods when called from the Java classes you are trying to test.

Wednesday, 27 February 2013

JSON, POJOs and Groovy's HTTPBuilder class


Whilst previous posts have looked at the use of RESTClient, this post will concentrate on its parent class, HTTPBuilder and also touch briefly on the JSONBuilder class. This post will cover the building and parsing of JSON, converting JSON to and from POJOs, adding authentication headers and handling response success and failures.

The HTTPBuilder class provides an API to make HTTP requests and parse the responses, and builds upon the Apache AbstractHttpClient class. The HTTPBuilder class can be instantiated with a URI to be used as default for all request methods. A default content type can also be supplied but for this example it will be the instantiated default of ContentType.ANY.

The below method takes an AccountFilter POJO, creates a JSONBuilder object and instantiates a HTTPBuilder with the default URI. It then makes a request specifying the method (POST) and contentType (JSON). The request body will be converted to a url-encoded form string. The code also shows how headers can be set, in this case, X-Application and X-Authentication using instance variables.


NOTE: The request is retrieving data and not creating or updating data, so would normally be a GET but the criteria can be complex and nested and therefore needs to be sent as JSON in the request body, hence the use of POST. (There is whole discussion on the web about GET requests containing a request body.) 


Set<Customer> listCustomers(AccountFilter accountFilter) {

    def resultList = new ArrayList<Customer>()
    def builder = new JsonBuilder(accountFilter)
    def http = new HTTPBuilder( 'https://beta-api.city81.com/json-rpc' )

 
    // POST request
    http.request (POST, JSON){req->
        body = [
            "jsonrpc" : "2.0",
            "method" : "Bank/v1.0/listCustomers",
            "params" : builder.toString(),
            "id" : 1
        ]

        headers.'X-Application' = this.applicationKey
        headers.'X-Authentication' = this.sessionToken

        // success response handler 
        response.success = { resp, json ->
            json.result.each { 
                resultList.add(new Customer(
                    it.customer.id, it.customer.name))
            }
        }

        // failure response handler 
        response.failure = { resp ->
            println "Unexpected error: ${resp.statusLine.statusCode}"
            println ${resp.statusLine.reasonPhrase}
        }
    }

    return resultList
}

The success response handler will iterate over the JSON result set and create Customer objects adding them to a collection. The failure response handler will print out the status code and reason phrase for the failure of the POST request.

As mentioned above, the code uses JSONBuilder to turn the POJO into JSON. For a Filter POJO with the field accountIds set, the JSON would appear as below:


"params":{"filter":{"accountIds":[1]}}

There is a flaw with this though in that if the filter attribute of MarketFilter is not set then the Filter tag will not be present in the JSON. If the service you are POSTing to requires this as its mandatory (albeit empty) then the req body params line would be:

"params" : [filter:[:]],

which would generate JSON:

"param": {"filter": {} }

This post covers only a small part of HTTPBuilder and one of a number of ways of POSTing a request, generating the JSON and parsing its response, many of which have been simplified with the RESTClient class.

The POJOS used in the above example:

public class Customer {

    private String id;
    private String name;

    public Customer(String id, String name) {
        this.id = id;
        this.name = name;
    }
   
    // ... getters and setters
 }


 public class AccountFilter {

    private Filter filter;
  

    public AccountFilter(Filter filter) {
        this.filter = filter;
    }
   
    // ... getters and setters
 }


public class Filter {
  

    private Set<String> accountIds;

    // ... getters and setters
}

Monday, 26 November 2012

Groovy's RESTClient with Spock Extensions

Groovy has an extension to its HTTPBuilder class called RESTClient which makes it fairly easy to test a RESTful web service. This post will describe how to use the RESTClient, how to inject it into a Groovy Spock test class via custom annotations, how to swap the injected client between a real one and a mocked one using Mockito, and how to test it.

The RESTClient class provides methods to perform the HTTP's GET, POST, PUT and DELETE methods. The RESTClient instance can be constructed with a url string and optionally a content type. The HTTP methods can then be called with a named parameter list. For example, the below code will create a RESTClient instance and call the get method passing in the path and a path parameter:

def client = new RESTClient("http://localhost:8080/testWebService/")
def resp = client.get(path : "server/status/ServerOne")

The response returned is an instance of HttpResponseDecorator. This wraps the HttpResponse to allow for easy access to the data which is parsed depending upon the content type. The below class shows it's use within a Spock framework test and how the parsed data can be extracted from the response object. The returned XML is:

<serverstatus>
  <servername>ServerOne</servername>
  <isrunning>true</isrunning>
</serverstatus>

then

<serverstatus>
  <servername>ServerTwo</servername>
  <isrunning>false</isrunning>
</serverstatus>


import groovy.util.slurpersupport.GPathResult
import groovyx.net.http.RESTClient
import spock.lang.*
import groovyx.net.http.ContentType

class InjectedRestServiceTest extends Specification {
 
 @RESTWebClient
 def client
   
 def "Test Server Statuses"() {

  when: "retrieve server status"
  def resp1 = client.get(path : "server/status/ServerOne")
  def resp2 = client.get(path : "server/status/ServerTwo")
  
  then: "test server one response"
  assert resp1.data.serverName.text() == "ServerOne"
  assert resp1.data.isRunning.text() == "true"
  
  then: "test server two response"
  assert resp2.data.serverName.text() == "ServerTwo"
  assert resp2.data.isRunning.text() == "false"
  
 }
 
}

The test asserts are purely there in this example to prove that the response from the real web service are the same as from the mocked web service. In reality, you would be testing some other functionality of which calling a RESTful web service is part of the flow. You wouldn't really assert the response from a mocked RESTClient although you may want to verify that it has been called n times.

The InjectedRestServiceTest class has the RESTClient injected using the custom made annotation @RESTWebClient. There are three classes involved which enable this functionality. First the interface, which is marked as only visible at field level, contains an @ExtensionAnnotation specifying the class which basically tells Spock what to do when it encounters the @RESTClient annotation:

import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import org.spockframework.runtime.extension.ExtensionAnnotation

@Retention(RetentionPolicy.RUNTIME)
@Target([ ElementType.FIELD ])
@ExtensionAnnotation(RESTWebClientAnnotationDrivenExtension)

public @interface RESTWebClient{}
The RESTWebClientAnnotationDrivenExtension class in this example only needs to override the visitFieldAnnotation method of the AbstractAnnotationDrivenExtension class because of the annotation's target element type:

import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.extension.AbstractMethodInterceptor
import org.spockframework.runtime.extension.IMethodInvocation
import org.spockframework.runtime.model.FieldInfo
import org.spockframework.runtime.model.SpecInfo

class RESTWebClientAnnotationDrivenExtension extends 
 AbstractAnnotationDrivenExtension<RESTWebClient> {

 private static final String url = 
  System.getProperties().getProperty("url")
 private static final boolean useMock = 
  Boolean.parseBoolean(System.getProperties().getProperty("useMock"))
 
 @Override
 void visitFieldAnnotation(RESTWebClient annotation, FieldInfo field) {
  def methodInterceptor = new RESTWebClientMethodInterceptor(field, url, useMock)
  methodInterceptor.install(field.parent)
 }
 
}

The above extension tells Spock that when it encounters @RESTClient, it must create an interceptor with the arguments denoting the field to which the RESTClient instance is to be set, the url of the web service and a boolean to specify if the mock is to be used or the real web service should be used. (The meaning of the two values could be combined to just have a url and if set then the real web service must be used.)

The interceptor will create either a mock RESTClient or a real one. It will do this when the client field needs to be set in the InjectedRestServiceTest class. If the real RESTClient is required then an instance using the url will be created and set using the fieldInfo.writeValue method. If a mock is required then a Mockito mock RESTClient is created and the expected behaviour mocked using instances of the class HttpResponseDecorator (although these could be mocks too.) Again, this is set using the fieldInfo.writeValue method. The mocking code should probably be in its own method or even class but for this simple example its within the setupRESTClient method:

import groovy.mock.interceptor.MockFor
import groovyx.net.http.HttpResponseDecorator
import groovyx.net.http.RESTClient
import org.spockframework.runtime.extension.AbstractMethodInterceptor;
import org.spockframework.runtime.extension.IMethodInvocation;
import org.spockframework.runtime.model.FieldInfo;
import org.spockframework.runtime.model.SpecInfo;

import static org.mockito.Mockito.*
import static org.mockito.Matchers.*

class RESTWebClientMethodInterceptor extends AbstractMethodInterceptor {

 private final FieldInfo fieldInfo
 private final String url
 private final boolean useMock

 RESTWebClientMethodInterceptor(FieldInfo fieldInfo, String url, boolean useMock) {
  this.fieldInfo = fieldInfo
  this.url = url
  this.useMock = useMock
 }

 @Override
 void interceptSetupMethod(IMethodInvocation methodInvocation) {
  setupRESTClient(methodInvocation.target)
  methodInvocation.proceed()
 }

 @Override
 void install(SpecInfo specInfo) {
  specInfo.setupMethod.addInterceptor this
 }

 private void setupRESTClient(target) {

  if (useMock) {

   def xmlServerOne =
     """
      <serverStatus>
        <serverName>ServerOne</serverName>
        <isRunning>true</isRunning>
      </serverStatus>
      """

   def xmlServerTwo =
     """
      <serverStatus>
        <serverName>ServerTwo</serverName>
        <isRunning>false</isRunning>
      </serverStatus>
      """

   def httpResponseDecoratorServerOne = new HttpResponseDecorator(
     null, new XmlParser().parseText(xmlServerOne))
   def httpResponseDecoratorServerTwo = new HttpResponseDecorator(
     null, new XmlParser().parseText(xmlServerTwo))
   def mapServerOne = [path:"server/status/ServerOne"]
   def mapServerTwo = [path:"server/status/ServerTwo"]

   RESTClient mockRESTClient = org.mockito.Mockito.mock(RESTClient)
   when(mockRESTClient.get(mapServerOne)).thenReturn(httpResponseDecoratorServerOne);
   when(mockRESTClient.get(mapServerTwo)).thenReturn(httpResponseDecoratorServerTwo);

   fieldInfo.writeValue(target, mockRESTClient)
  }
  else {
   fieldInfo.writeValue(target, new RESTClient(url))
  }
 }
 
}

Whilst there are different ways to mock and test RESTful web services, the above classes do show how easily RESTful web services can be called and their responses parsed, how annotations can be created and how objects can be mocked. Similar examples could be constructed for different aspects of a Spock test eg features, fixtures etc..

More can be found on extensions at http://code.google.com/p/spock/wiki/SpockBasics#Extensions and on RESTClient at http://groovy.codehaus.org/modules/http-builder/doc/rest.html

Friday, 14 September 2012

Using Spock to test Spring classes

As the previous post mentioned, Spock is a powerful DSL built on Groovy ideal for TDD and BDD testing and this post will describe how easy it is to use Spock to test Spring classes, in this case the CustomerService class from the post Using Spring Data to access MongoDB. It will also cover using Spock for mocking.

Spock relies heavily on the Spring's TestContext framework and does this via the @ContextConfiguration annotation. This allows the test specification class to load an application context from one or more locations.

This will then allow the test specification to access beans either via the annotation @Autowired or @Resource. The test below shows how an injected CusotmerService instance can be tested using Spock and the Spring TestContext: (This is a slightly contrived example as to properly unit test the CustomerService class as you would create a CustomerService class in the test as opposed to one created and injected by Spring.)

package com.city81.mongodb.springdata.dao

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration

import spock.lang.*

import com.city81.mongodb.springdata.entity.Account
import com.city81.mongodb.springdata.entity.Address
import com.city81.mongodb.springdata.entity.Customer

@ContextConfiguration(locations = "classpath:spring/applicationContext.xml")
class CustomerServiceTest extends Specification {

 @Autowired
 CustomerService customerService
 
 
 def setup() {
  customerService.dropCustomerCollection()
 } 
 
 def "insert customer"() {
  
  setup:
  
  // setup test class args
  Address address = new Address()
  address.setNumber("81")
  address.setStreet("Mongo Street")
  address.setTown("City")
  address.setPostcode("CT81 1DB")
 
  Account account = new Account()
  account.setAccountName("Personal Account")
  List<Account> accounts = new ArrayList<Account>()
  accounts.add(account)
  
  Customer customer = new Customer()
  customer.setAddress(address)
  customer.setName("Mr Bank Customer")
  customer.setAccounts(accounts)

  when:
  customerService.insertCustomer(customer)
  
  then:
  def customers = customerService.findAllCustomers()
  customers.size == 1
  customers.get(0).name == "Mr Bank Customer"
  customers.get(0).address.street == "Mongo Street"
  
 } 
}

The problem though with the above test is that MongoDB needs to be up and running so to remove this dependency we can Mock out the interaction the database. Spock's mocking framework provides many of the features you'd find in similar frameworks like Mockito.

The enhanced CustomerServiceTest mocks the CustomerRepository and sets the mocked object on the CustomerService.

package com.city81.mongodb.springdata.dao

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration

import spock.lang.*

import com.city81.mongodb.springdata.entity.Account
import com.city81.mongodb.springdata.entity.Address
import com.city81.mongodb.springdata.entity.Customer

@ContextConfiguration(locations = "classpath:spring/applicationContext.xml")
class CustomerServiceTest extends Specification {

 @Autowired
 CustomerService customerService
 
 CustomerRepository customerRepository = Mock()
 
 def setup() {
  customerService.customerRepository = customerRepository
  customerService.dropCustomerCollection()
 } 
 
 def "insert customer"() {
  
  setup:
    
  // setup test class args
  Address address = new Address()
  address.setNumber("81")
  address.setStreet("Mongo Street")
  address.setTown("City")
  address.setPostcode("CT81 1DB")
 
  Account account = new Account()
  account.setAccountName("Personal Account")
  List<Account> accounts = new ArrayList<Account>()
  accounts.add(account)
  
  Customer customer = new Customer()
  customer.setAddress(address)
  customer.setName("Mr Bank Customer")
  customer.setAccounts(accounts)
  
  when:
  customerService.insertCustomer(customer)
  
  then:
  1 * customerRepository.save(customer)
  
 } 
 
 def "find all customers"() {
 
  setup:
  
  // setup test class args
  Address address = new Address()
  address.setStreet("Mongo Street")
  
  Customer customer = new Customer()
  customer.setAddress(address)
  customer.setName("Mr Bank Customer")
  
  // setup mocking  
  def mockCustomers = []
  mockCustomers << customer
  customerRepository.findAll() >> mockCustomers
  
  when:
  def customers = customerService.findAllCustomers()
  
  then:
  customers.size() == 1  
  customers.get(0).name == "Mr Bank Customer"
  
 }
 
}


The CustomerRepository is by way of name and type although it could be inferred by just the name eg

def customerRepository = Mock(CustomerRepository)

The injected customerRepository is overwritten by the mocked instance and then in the test setup, functionality can be mocked.

In the then block of the insert customer feature, the number of interactions with the save method of customerRepository is tested and in the find all customers feature, the return list of customers from the findAll call is a mocked List,as opposed to one retrieved from the database.

More detail on Spock's mocking capabilities can be found on the project's home page.


Tuesday, 11 September 2012

Testing Java using the Spock Framework

The testing framework Spock is a powerful DSL built on Groovy which enables easily writable and extremely readable tests which lends itself well to software development processes like TDD and BDD.

As an example of how much clarity Spock and Groovy bring to testing I'll compare it with the tests for the ForkJoinPool example class InterpolationService.

Every Spock test (or specification) extends spock.lang.Specification. This abstract class uses Spock's JUnit runner, org.spockframework.runtime.Sputnik, and contains useful methods for writing tests eg creating mock objects

There are four main aspects to a specification: Fields, Fixtures, Features and Helpers. There is no point going into depth on each as there are well documented on the Spock Basics wiki but we'll certainly concentrate on Features with respect to the comparison with the JUnit InterpolationService tests.

Features are instance methods which must contain at least one block where a block is one of six methods: setup, when, then, expect, cleanup and where. The blocks setup and cleanup are as you might expect but others are described in more detail below.

First though and the first few lines of the InterpolationServiceTest are shown below:

package com.city81.interpolation.service

import spock.lang.*

class InterpolateServiceTest extends Specification {

    @Shared def interpolateService = new InterpolateService()

The class under test ie InterpolationService is marked as being @Shared (ie all features use the same instance).  This annotation is ideal for expensive resources like a DB connection.

Next the first feature which tests the interpolation of two numbers with an even number of steps:

def "interpolate two numbers with even no. of steps"() {
            
            expect:
            interpolateService.interpolate(a, b, c) == d
            
            where:
            a   | b    | c | d
            5.0 | 25.0 | 4 | [5.0, 10.0, 15.0, 20.0, 25.0] 
            }


The equivalent JUnit assert test method would be:

@Test
 public void testInterpolateTwoValues() {
     List<Double> interpolatedList = 
         interpolateService.interpolate(5.0, 25.0, 4);

    assertEquals(5, interpolatedList.size());
    assertEquals(5.0, interpolatedList.get(0), 0);
    assertEquals(10.0, interpolatedList.get(1), 0);
    assertEquals(15.0, interpolatedList.get(2), 0);
    assertEquals(20.0, interpolatedList.get(3), 0);
    assertEquals(25.0, interpolatedList.get(4), 0);
}


The clarity in what you are testing is easy to see and it is so easy with Spock just to add another where line to test another scenario:

def "interpolate two numbers with even no. of steps"() {

            expect:
            interpolateService.interpolate(a, b, c) == d

            where:
            a   | b    | c | d
            5.0 | 25.0 | 4 | [5.0, 10.0, 15.0, 20.0, 25.0] 
            2.0 | 14.0 | 6 | [2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0] 
            }

It is also worth noting at this point that Groovy uses BigDecimal for floating number numbers but if you want to denote doubles then numbers can be appended with a d eg 5.0d, -15.0d

Whilst the above demonstrates the expect and where blocks, the below shows how to test for exceptions using when and then blocks:

def "interpolate two numbers where first arg is null"() {
       
            when:
            interpolateService.interpolate(null, 25.0, 4)

            then:
            thrown(IllegalArgumentException)
            }

An alternative approach to using thrown(IllegalArgumentException) could be to bind a variable to access the exception:

then:
            IllegalArgumentException e = thrown()
            e.cause == InterpolateService.VALUE_ARG_NOT_NULL_EXCP_TEXT

You can also use the when and then blocks to order interactions although not if the first when throws an exception.

There is much more on Spock on it's Google code pages and the above only describes a few basics but the next post will look at Mocking and also examine it's use with Spring by testing code from a previous post. Might even get the Groovy code formatting working by then too!

Friday, 13 July 2012

Using Spring Data to access MongoDB

Spring Data is the data access Spring project for integrating with data stores. This post will cover the Spring Data sub project for accessing the document store MongoDB. It follows on from the Morphia post by showing how Spring Data for MongoDB would persist and query the same POJOs.

The four domain objects are shown below:

package com.city81.mongodb.springdata.entity;

import org.springframework.data.annotation.Id;

public abstract class BaseEntity {

 @Id
 protected String id;
 private Long version;

 public BaseEntity() {
  super();
 }

 public String getId() {
  return id;
 }

 public void setId(String id) {
  this.id = id;
 }

 public Long getVersion() {
  return version;
 }

 public void setVersion(Long version) {
  this.version = version;
 }

}


package com.city81.mongodb.springdata.entity;

import java.util.List;

import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Customer extends BaseEntity {

 private String name;
 private List<Account> accounts;
 private Address address;
 
 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 } 
  
 public List<Account> getAccounts() {
  return accounts;
 }

 public void setAccounts(List<Account> accounts) {
  this.accounts = accounts;
 }

 public Address getAddress() {
  return address;
 }

 public void setAddress(Address address) {
  this.address = address;
 }
 
}

package com.city81.mongodb.springdata.entity;


public class Address {

 private String number;
 private String street;
 private String town;
 private String postcode;
 
 public String getNumber() {
  return number;
 }
 public void setNumber(String number) {
  this.number = number;
 }
 public String getStreet() {
  return street;
 }
 public void setStreet(String street) {
  this.street = street;
 }
 public String getTown() {
  return town;
 }
 public void setTown(String town) {
  this.town = town;
 }
 public String getPostcode() {
  return postcode;
 }
 public void setPostcode(String postcode) {
  this.postcode = postcode;
 }
}

package com.city81.mongodb.springdata.entity;

import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Account extends BaseEntity {

 private String accountName;
 
 public String getAccountName() {
  return accountName;
 }

 public void setAccountName(String accountName) {
  this.accountName = accountName;
 }

}

Some classes are marked with the @Document annotation. This is optional but does allow you to provide a collection name eg

@Document(collection="personalBanking")


Also in the Customer class, the Address and Accounts attributes would be stored as embedded within the document but they can be stored separately by marking the variables with @DBRef. These objects will then be eagerly loaded when the Customer record is retrieved.

Next, using XML based metadata to register a MongoDB instance and a MongoTemplate instance.

The MongoFactoryBean is used to register an instance of com.mongodb.Mongo. Using the Bean as opposed to creating an instance of Mongo itself ensures that the calling code doesn't have to handle the checked exception UnknownHostException. It also ensures database specific exceptions are translated to be Spring exceptions of the DataAccessException hierarchy.

The MongoTemplate provides the operations (via the MongoOperations interface) to interact with MongoDB documents. This thread safe class has several constructors but for this example we only need to call the one that takes an instance of Mongo and the database name.

Whilst you could call the operations on the MongoTemplate to manage the entities, there exists a MongoRepository interface which can be extended to be 'document' specific via the use of Generics. The <mongo:repositories base-package="com.city81.mongodb.springdata.dao" />   registers beans extending this interface.



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:mongo="http://www.springframework.org/schema/data/mongo" 
 xsi:schemaLocation="http://www.springframework.org/schema/beans        
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd         
  http://www.springframework.org/schema/context         
  http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/data/mongo 
  http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">    
  
 <context:annotation-config />
  
 <context:component-scan base-package="com.city81.mongodb.springdata" />
 
 <!-- MongoFactoryBean instance --> 
 <bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean">
  <property name="host" value="localhost" />
 </bean>   
 
 <!-- MongoTemplate instance --> 
 <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
  <constructor-arg name="mongo" ref="mongo" />
  <constructor-arg name="databaseName" value="bank" />
 </bean> 
 
 <mongo:repositories base-package="com.city81.mongodb.springdata.dao" />
  
</beans>

The repository class in this example is the CustomerRepository. It gets wired with the MongoTemplate so provides the same (this time implicit type safe) operations but also provides the ability to add other methods. In this example, a find method has been added to demonstrate how a query can be built from the method name itself. There is no need to implement this method as Spring Data will parse the method name and determine the criteria ie findByNameAndAddressNumberAndAccountsAccountName will return documents where the customer name is equal to the first arg (name), and where the customer address number is equal to the second arg (number) and where the customer has an account which has an account name equal to the thrid arg (accountName).


package com.city81.mongodb.springdata.dao;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import com.city81.mongodb.springdata.entity.Customer;

@Repository
public interface CustomerRepository extends MongoRepository<Customer,String> {
 
 List<Customer> findByNameAndAddressNumberAndAccountsAccountName(
   String name, String number, String accountName);
 
}


In this example, we'll add a service layer in the form of the CustomerService class, which for this simple example just wraps the repository calls. The class has the CustomerRepository wired in and this service class is then in turn called from the Example class, which performs similar logic to the Morphia Example class.

package com.city81.mongodb.springdata.dao;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.city81.mongodb.springdata.entity.Customer;

@Service
public class CustomerService {  

 @Autowired
 CustomerRepository customerRepository;
  
 public void insertCustomer(Customer customer) {    
  customerRepository.save(customer);
 }
 
 public List<Customer> findAllCustomers() {           
  return customerRepository.findAll();
 }       
 
 public void dropCustomerCollection() {        
  customerRepository.deleteAll();   
 } 
 
 public List<Customer> findSpecificCustomers(
   String name, String number, String accountName) {           
  return customerRepository.findByNameAndAddressNumberAndAccountsAccountName(
    name, number, accountName);
 } 
 
}

package com.city81.mongodb.springdata;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.city81.mongodb.springdata.dao.CustomerService;
import com.city81.mongodb.springdata.entity.Account;
import com.city81.mongodb.springdata.entity.Address;
import com.city81.mongodb.springdata.entity.Customer;

public class Example {
 
 public static void main( String[] args ) {     
  
  ConfigurableApplicationContext context
   = new ClassPathXmlApplicationContext("spring/applicationContext.xml");       
  
  CustomerService customerService = context.getBean(CustomerService.class);       
  
  // delete all Customer records
  customerService.dropCustomerCollection();  
  
     Address address = new Address();
     address.setNumber("81");
     address.setStreet("Mongo Street");
     address.setTown("City");
     address.setPostcode("CT81 1DB");
    
     Account account = new Account();
     account.setAccountName("Personal Account");
     List<Account> accounts = new ArrayList<Account>();
     accounts.add(account);
     
     Customer customer = new Customer();
     customer.setAddress(address);
     customer.setName("Mr Bank Customer");
     customer.setAccounts(accounts);
          
     // insert a Customer record into the database
  customerService.insertCustomer(customer);          
     
     address = new Address();
     address.setNumber("101");
     address.setStreet("Mongo Road");
     address.setTown("Town");
     address.setPostcode("TT10 5DB");
    
     account = new Account();
     account.setAccountName("Business Account");
     accounts = new ArrayList<Account>();
     accounts.add(account);
     
     customer = new Customer();
     customer.setAddress(address);
     customer.setName("Mr Customer");
     customer.setAccounts(accounts);  

     // insert a Customer record into the database
  customerService.insertCustomer(customer);     
      
  // find all Customer records
  System.out.println("\nALL CUSTOMERS:");
  List<Customer> allCustomers = customerService.findAllCustomers();     
  for (Customer foundCustomer : allCustomers) {
   System.out.println(foundCustomer.getId() + " " + foundCustomer.getName());   
   System.out.println(foundCustomer.getAddress().getTown());   
   System.out.println(foundCustomer.getAccounts().get(0).getAccountName() + "\n");   
  }
  
  // find by customer name, address number and account name
  System.out.println("\nSPECIFIC CUSTOMERS:");  
  List<Customer> specficCustomers = customerService.findSpecificCustomers(
    "Mr Customer","101","Business Account");     
  for (Customer foundCustomer : specficCustomers) {
   System.out.println(foundCustomer.getId() + " " + foundCustomer.getName());   
   System.out.println(foundCustomer.getAddress().getTown());   
   System.out.println(foundCustomer.getAccounts().get(0).getAccountName() + "\n");   
  }
  
 } 
 
}
The output from the above would look similiar to the below:

04-Oct-2012 13:48:48 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@15eb0a9: startup date [Thu Oct 04 13:48:48 BST 2012]; root of context hierarchy
04-Oct-2012 13:48:48 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring/applicationContext.xml]
04-Oct-2012 13:48:48 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1c92535: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,customerService,mongo,mongoTemplate,customerRepository,org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor#0]; root of factory hierarchy

ALL CUSTOMERS:
506d85b115503d4c92392c79 Mr Bank Customer
City
Personal Account

506d85b115503d4c92392c7a Mr Customer
Town
Business Account


SPECIFIC CUSTOMERS:
506d85b115503d4c92392c7a Mr Customer
Town
Business Account


This is a brief overview of Spring Data from MongoDB but there are many other facets to this project including MappingConverters, Compound Indexes and MVC support. For more info http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/