This is my first meaningful blog post and is inspired by
This Stack Overflow question. I answered the question but wasn’t entirely happy with it (and apparently
neither was the community!). So I decided to look at the problem in more depth
as I expect to use Akka Http in the future, and mocking is an important part
of testing components which connect to external services.
In this case, the client is to connect to the
Police UK Data service for street level crimes,
chosen because it is a freely available data API, and fairly interesting,
so it’s a reasonable target for a programmatic client.
The output is a list of ‘Crime’ objects. For simplicity the client does not attempt to model
the entire set of fields per Crime, but aims to populate the following case class:
The full deserialization code (which uses Spray JSON) is not shown here
since it is not the focus of this post, but the full source is available
on Github.
In order to loosely couple the Http logic from the business logic, the
Cake Pattern
is used. First we have an Http Service trait:
Since this is just a simple example, the makeRequest method takes a String URI
and returns a Future containing the response text from the server, also as a String.
Since a future can complete with Success or Failure,
it can represent any invalid HTTP responses
as failures. In a more complex scenario, a richer method signature would probably
be required, for example allowing the HTTP Method to be set, and allowing content to
be provided e.g. for PUT and POST requests. And it would make sense to return some
model object representing the Http response rather than just a String. However,
it is advisable not to design the signature to return the HttpResponse object from
a particular HTTP library, such as akka.http.scaladsl.model.HttpResponse, since
this makes it harder to switch out one HttpRequestService for another based
on a different HTTP Client library, and some of the benefit of the Cake Pattern would
be lost.
Next, we create a Trait representing the service implementing the API specific
logic:
The following line is the essence of the Cake Pattern:
this: HttpRequestService =>
It is called a “Self-type annotation” and it means that any class or object
which extends the PoliceUKDataService trait must also extend the
HttpRequestService trait (or a sub-trait of it). Essentially it dictates that
the PoliceUKDataService must always have access to the behaviour of an
HttpRequestService and an attempt to use PoliceUKDataService without mixing
in an HttpRequestService would cause a compile time error.
The implicit ExecutionContext is necessary since we are mapping the result of
the Future returned from makeRequest. Any operation with a Future
requires one.
It is desirable to unit test the getData method of PoliceUKDataService
without calling out to the Police Service API. Being able to do so is the
reason for this post. It is possible to go ahead and implement tests for it even before
having any working implementation of an HttpRequestService.
Here, ScalaMock is used to create a mock of the makeRequest
method.
By implementing makeRequest as a mock, we can dictate what it returns for a given
input. Simple! Further tests e.g. for failure cases could be implemented in the same
way.
Now we have confidence in the correctness of our API Service, we can implement
our client application using Akka Http. First an implementation of the Http Service:
Then we mix the API service and Akka Http Service together to build a client:
The full source code for this post is available on Github.