So before I get too deep into this topic, I have found with the myriad of available Spring components and modules, there is always a few ways to do things.  It all comes down to what you learn and what works for you.  If there are better ways to work my examples, then absolutely, drop me a note, I am eager to learn.

Anyways, I found I spent quite a bit of time trying to understand how to test my Spring Boot RESTful service based on an Integration test.  Please note you can setup tests using MockMvc, but I found the RestTemplate (along with TestRestTemplate) to be easier to code and less verbose. I found with MockMvc, I had to work in a deeper layer using JSON like Strings and then Jackson to map the JSON data.  Using RestTemplate, I could easily work with Objects and let the Spring and Jackson libraries do their thing.  But feel free to do whatever you prefer.

Let’s consider a simple example of a TODO list.  We want to post a TODO item to the database via a REST call.  We also want to also create an Integration tester using the Spring Boot JUnit framework but we don’t want to do simply a Unit Test, we want a full integration test without having to run some web client (ie postman) so our server starts up on some port and we hit it with a real REST request, no mocking here.  To add some additional reference to our example, we will assume all TODOObjects get assigned to a User, thus our REST API will always include a User ID.

Let’s consider the RESTful API for the POST method.  Assume our tester will make a post request (passing JSON via Postman or a JS API) but since we are integration testing right now, we can use a Java object.  Our API in our Rest Controller may look like this:

@RequestMapping(value = "/api/{userId}/todo/", method = RequestMethod.POST)
 public ResponseEntity<TODO> create(@PathVariable Long userId, @RequestBody TODO todo) {
 HttpStatus status = HttpStatus.CREATED; // Assume success for our simple test
TODO result = ourTODOServiceAPI.create(userId,todo);//Do whatever here
 return new ResponseEntity<TODO>(result, status);
 }

So this is a very simple test simply so you can code up the entry point on the REST Controller.  The key items to watch here are the @PathVariable as this value is right inside of the REST API (ie the User ID) and the @RequestBody annotation which is also very important.  This killed me in the earlier days where I did not include this annotation.  The issue is, if you don’t include it, the BeanUtils cannot locate the correct mappers to map the JSON data to the object via the available constructors.  So when no mapping can be done, the default constructor is used and the data from the JSON request data is not mapped.  So just remember that annotation in your code.  In addition, I found I always had to include the slash on the end of the RequestMapping.  Perhaps there are things I am doing wrong with the URL, but if I needed the API to match up correctly, I always use the ending / on the path.  Seems so trivial, but it did lead to problems with my code along the way.

Before we explain the details of the POJO object which I found to be critical, let’s consider the Integration tester.

 @Test
 // Ok don't take this test case literally.  I am being lazy by using a test to show different conditions for demo purposes only.
 public void testTODOCreateSuccess() throws Exception {
 RestTemplate restTemplate = new RestTemplate();
 
 HttpHeaders headers = new HttpHeaders();
 String auth = "userid" + ":" + "password";
 byte[] encodedAuth = Base64.encode(auth.getBytes(Charset.forName("US-ASCII")));
 String authHeader = "Basic " + new String( encodedAuth );
 headers.set("Authorization", authHeader );
 
 TODO todo = new TODO(1L, "Get Groceries");
 HttpEntity<TODO> httpEntity = new HttpEntity<TODO>(entity, headers);
 
 ResponseEntity<String> result = restTemplate.postForEntity("http://localhost:8080/api/1/todo/", httpEntity, String.class);
 String responseBody = result.getBody();
 ObjectMapper mapper = new ObjectMapper();
 todo = null;
 if (result.getStatusCode() == HttpStatus.BAD_REQUEST) {
 List<ErrorInfo> errors = mapper.readValue(responseBody, List.class);
 // perhaps more assertions??
 } else {
 todo = mapper.readValue(responseBody, TODO.class);
 }
 
 Assert.assertNotNull(todo);
 // Yes you absolutely need better testing here, but this is a demo
 }

Ok, now first things first, this is not a well coded unit/integration tester.  It’s simply here to illustrate some techniques in one test case.  You will want to rework this.  For example, we would not have conditions in our tester for bad requests and then choose whether to go down one path or another.  We would want a separate test case for all scenarios along with the appropriate Assertions.  I just want to show you all flows that “could” occur during a POST. Let’s walk through this example from the top.

  1. Headers are completely optional but if you are using something like Basic Authentication, you will need to pass this information over. I must note that you can also use the Spring TestRestTemplate object as it accepts a userID and password if you don’t want to code up all of that basic auth code.  So simply remove any references to headers if you don’t care about this.
  2. We create a simple TODO object and shove some information in it.  For now, assume the object only contains the User ID and the Name of the TODO item.  This becomes very important later on when your app can be more complex and the POJO itself has many instance variables.
  3. We package the headers and the TODO pojo into the HttpEntity wrapper class.  The reason we do this is the HttpEntity allows us to package headers in the request.
  4. We call the postForEntity method on the RestTemplate.  There is a corresponding postForObject but that will only return the object instance being returned from the REST call.  We need to be able to get the HttpStatus code along with the response, so we use the postForEntity.  The parameters are the REST API URL, the HttpEntity wrapper class that has our request object (TODO) and the headers (basic auth info) along with the response type we are expecting. I’ll explain why this is a String and not the entity itself in the next paragraph.  Hang tight for now.

Ok so those steps help us execute the REST service and get back a String response.  We need to stop here for a second and understand why we are using a String class.  First, let’s understand the POST REST call a bit more.  When a successful post request is made, we should always get back the CREATED HttpStatus code (201).  In addition, we always return the object with the new ID generated from the insert in the object.  It’s good practice to also include the newly created object REST URI in the header of the response, kind of like dynamic documentation, but we are not showing that in the example above.  That still does not explain why we are returning a String class when it’s clear we can see our initial REST call returns the TODO object, not a string. Well, consider this.  Assume there is a problem creating the object, or validation fails on that object.  Maybe they don’t pass in one of the required values (ie a null name).  We don’t want to simply throw an exception and let the calling client handle the ugly error where there is very little information to guide them what went wrong.  We may want some informative information (such as the field that has the issue along with a message).  However, even in our simple example, we don’t see anywhere that handles this behavior.  So let’s now introduce the concept of handling Exceptions when something goes wrong with our REST call.  What we can do, along with useful libraries such as the Hibernate Bean Validator, is to create a method (or many methods) on our RESTController such as this:

@ExceptionHandler(value=MethodArgumentNotValidException.class)
  public ResponseEntity<ErrorInfoList> handleValidationException(HttpServletRequest req, MethodArgumentNotValidException exception) {
  List<FieldError> errors = exception.getBindingResult().getFieldErrors();
  ErrorInfoList errorInfoList = new ErrorInfoList();
  for (FieldError fieldError : errors) {
     errorInfoList.getErrorInfos().add(new ErrorInfo(fieldError.getCode(), fieldError.getDefaultMessage(), fieldError));
  }
  return new ResponseEntity<ErrorInfoList>(errorInfoList, HttpStatus.BAD_REQUEST);
}

The @ExceptionHandler is a Spring class that annotates a method given some kind of exception occurs.  In our case, the value is set to the Spring exception MethodArgumentNotValidException.  Once this exception is thrown by Spring (we’ll explain how that happens shortly), the @ExceptionHandler will catch this exception, and we end up returning some custom List of ErrorInfo objects that we created ourselves.  Basically we want to track any and all errors on fields that were a problem, and send them back to the client app as JSON data (meaing the Spring FieldError class instances along with any other information you think the client may need for the best UX).

So now we can return all fields that have problems! Yay.  We don’t do anything with the Http Request object but we do get a handle to the Spring Exception that was thrown by the validation process (again this is coming, stay with me).  An HttpStatus code of BAD_REQUEST can be returned since Spring allows us to return a ResponseEntity instance.  As promised, to get Spring to fire the exception, we add the @Valid annotation on the @RequestBody of the object parameter (ie TODO) coming into the REST call:

public ResponseEntity<TODO> create(@PathVariable Long userId, @Valid @RequestBody TODO todo)

You’ll need to assign the @NotNull etc annotations to each instance variable you want validated.  Visit http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/ for more information on the different options.  What you are doing is embedding your validation logic easily right inside your POJO and Spring will perform all of that validation using the @Valid annotation, I know, wow!  That is bean validation using Hibernate and Spring Boot/REST.

Going back to our tester again, how can our single create API service return either the TODO object OR the List of ErrorInfos, they are completely different objects??  Thus the remainder of the Integration test code and also why we ended up returning a String.  Spring will convert the result of the API call into some kind of JSON result.  Since we don’t know what is in that result at runtime (ie Errors or the TODO Object), we must first ask the ResponseEntity we got back from the postForEntity what was your HttpStatus code.  If the code was a BAD_REQUEST, then we know we are getting back a list of Error Infos.  However, if the HttpStatus code was 201 (CREATED), then we can use Jackson to map the String back to the object we expected, or a TODO object.

That is what this code is doing but as I mentioned prior, it’s not practical in a JUnit tester:

String responseBody = result.getBody();
 ObjectMapper mapper = new ObjectMapper();
 todo = null;
 if (result.getStatusCode() == HttpStatus.BAD_REQUEST) {
 List<ErrorInfo> errors = mapper.readValue(responseBody, List.class);
 // perhaps more assertions??
 } else {
 todo = mapper.readValue(responseBody, TODO.class);
 }

Realistically, you would have a success and failure test cases that knew what kind they were getting back.  You would never use conditions like I did.

So the last thing to remember is your constructors on your POJO.  I found I needed to create a default constructor for the POJO and although technically, I know you should not be able to create a Bean unless you have all required information, I found it was just easier to create the default constructor and use bean validation annotations on each instance variable.  Spring does a matching of fields to constructors when objects and JSON is mapped, so be careful on that front.  You may need to do a bit of tweaking, but generally I had to have the default constructor.  I also found that it was better to have 2 bean types, the web based POJO and the JPA entity bean.  I think it’s better design to separate these object types even if it’s a bit more work to have somewhat duplicate objects.

One final note around integration testing.  Many developers like to use a tools such as Postman to test their RESTful services. Just remember, when you are testing a POST with REST, typically you are exchanging JSON data between the client and the server, so make sure you set the Body on Postman (a tab called Body) to raw and application/json from the drop down selection.  I believe the default is form-data and unless you configure your REST controller to accept form posts, you’ll get an exception when you try to invoke the REST service when you are not sending JSON.  Just a heads up, perhaps it’s obvious for you, but I had some troubles when I was trying it out.