… or my urge to share knowledge

Testing error handling in RESTful application with jersey and jbehave

As far as I’m concerned I write tests mostly for one reason : deal with the non-nominal path because in most cases the nominal path is the easier one. They help me dealing with the question “What if the user/client doesn’t provide correct inputs how will my application behave ?“

RESTful applications are no exceptions. How do we manage non-nominal cases ?
As REST is built on top of HTTP a great amount of work has already been done with error semantics.
Still, that semantic will never match your application’s. Neither will it match your platform native error mechanism.
It’s a classic issue that I want to address for RESTful applications.

1. Carefully design error mapping based on error semantics

In HTTP error semantics give an indication on the error type. They are represented by status codes.
That system helps categorizing responses and specially errors. For example all 4XX status codes are considered errors in HTTP and 404 are “Not found” errors.
It can lead us to imagine a simple yet already powerful java-http error mapping :

Bad request (400) : IllegalArgumentException. I usually throw this exception when the user input doesn’t allow me to correctly process the request; be it a wrong data format (null, empty, incorrect date pattern, etc) or an unsatisfied business rule

Access denied (403) : AccessDeniedException. Thrown by spring security when good credentials are provided but user doesn’t have enough rights for this request

Not found (404) : null result on “find by id” requests. Not to be confused with empty search result which should return 200 and empty results

Internal server error (500) : IllegalStateException. The container returns a 500 by default (unhandled application errors) but an IllgalStateException should also lead to a 500 because the system state is instable

The above mapping can be refined as needed. But the idea is getting most of what already exists. Avoid creating ad-hoc exceptions.

2. Provide the correct error context via messages

The status code is fine but not enough because one status code corresponds to many contexts. I can throw a 404 for many reasons in my app. This is where messages come into play.
The message holds more context. A 404 error with this message “Product with reference ref-55555-ermcccvpsole-59999 was not found.” is much clearer and carries more valuable information than this one “404 – Not found”.

3. Implement i18n to improve your service

Sometimes you have to internationalize your application. HTTP defines a Accept-Language header which allows a client to specify its preferred response language. When the specified language is not supported by the server a good practice is to provide a fallback one.
Technically it’s supposed to be as simple as using properties files.
Things get rough when it comes to using UTF-8 encoding (the properties default encoding is ISO 8859-1) … They get even harder when some messages thrown by a third-party framework reaches the end user. Most time those messages are technical, and if you’re lucky enough they are in the same language as your application (which is unlikely).

4. Let’s get to work !

As usual, a simple feature gets richer and could use a set of scenarii. Here is my expected specification in natural language :

Different semantics should return different http codes

Same semantic with different contexts should return same http code and different messages

Same semantic with same context and different supported languages should return same http code and different messages

Same semantic with same context and different unsupported languages should return same http code and same messages

You know the rules (you can explore the code to understand jbehave configuration) : write plain text story, code story implementation, note the failure and change your application behaviour until story is done. Repeat this whenever you want to ensure/proove a behaviour. At the end you should get quite a covered software.

Plain text story : nothing new here, same old given/when/then.

...
Scenario: Same semantic with same context and different supported languages should return same http code and different messages
Given I receive data
Given I accept language
When I send a find advert by reference with reference .
Then I should get an unsuccessful response
And the response code should be 404
And the response message should be
Examples:
|responseContentType|responseLanguage|reference|message
|application/json|en|ref-55555-ermcccvpsole-59999|Advert with reference ref-55555-ermcccvpsole-59999 was not found
|application/json|fr|ref-55555-ermcccvpsole-59999|La référence ref-55555-ermcccvpsole-59999 est inconnue
...

Story implementation : constructs the http message (uri, headers, body), sends it and reads the response for assertions. We could use many http clients but why bother when jersey client suits ?

Then ExceptionMapper : @Provider automatically registers this bean as a jersey special bean. Implementing ExceptionMapper marks this bean as an exception handler. This is where you can build your response : status, body, cookies, headers, whatever pleases you. This really is priceless to me. It allows me to send custom error reponses to client (complex message structure instead of just a string, complex object type instead of plain text when needed, etc). It’s all well documented here.