Stubbing HTTP APIs and Microservices with the Hoverfly Java DSL
Following the latest release of Hoverfly Java, I would like to give an overview of the new DSL, which allows you to create stubs for HTTP APIs using Java code instead of JSON.
I will discuss some of the advantages of doing this and also some of the trade-offs, with comparisons to alternatives such as MockRestServiceServer, Wiremock and Betamax.
Overview
First of all, the library is available from Maven Central:
<dependency> <groupId>io.specto</groupId> <artifactId>hoverfly-java</artifactId> <version>0.9.1</version> <scope>test</scope> </dependency>
Or you can include it in your Gradle project:
testCompile "io.specto:hoverfly-java:0.9.1"
Currently, Hoverfly uses JSON for storing simulations. This works well when using the tool directly, but as a Java developer who wants to quickly set up some stubs for an external API or a microservice, it might be easier to do everything in code.
A basic example is as follows:
@ClassRule
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulateMode(dsl(
service("www.booking-service.com")
.get("/api/bookings/1")
.willReturn(success(json(new Booking(1))))
.post("/api/bookings")
.body(json(new Booking("myBooking")))
.willReturn(created(“http://www.booking-service.com/api/bookings/1”)),
service("www.payment-service.com")
.get("/api/payments/1")
.willReturn(success("{\"amount\": \"1.25\"\"}", "application/json"))
))
As you can see, the DSL is hierarchical, even down to how it is formatted by your IDE. This structure draws inspiration from the specification YML used by Swagger. You specify a host, followed by a method, followed by a path etc. The aim is to make the code expressive and human readable.
The DSL is also fluent, so building requests and responses after hitting the entrypoint of HoverflyDSL.service(host)
should be self-explanatory.
Assuming you are using an HTTP client that respects JVM proxy settings, then this is literally all you need to do. If you make a request which is satisfied by a matcher declared in the DSL, Hoverfly will respond pretending to be the real service.
Response Creators
When generating responses, it’s nice to have wrappers to build things that would be commonly be returned by an API. That’s why we have the ResponseCreators
class, which generates common responses:
.willReturn(created("www.location-header-value.com/foo"))
.willReturn(success("foo", "text/plain"))
.willReturn(badRequest())</pre>
Unfortunately, a lot of the APIs we deal with are “legacy”. That’s why these helper methods return a ResponseBuilder
that allows us to generate any response we want. You can also just instantiate it directly if you like:
.willReturn(response().status(100))
HTTP Body Converters
One problem with Java is lack of “first class citizenship” for JSON. When you want to specify JSON inline you have to deal with the mess of escape characters:
"{" +
"\"bookingId\":\"1\"," +
"\"origin\":\"London\"," +
"\"destination\":\"Singapore\"," +
"\"time\":\"2011-09-01T12:30\"," +
"\"_links\":{\"self\":{\"href\":\"http://localhost/api/bookings/1\"}}" +
"}");
That’s why people end up working with POJO’s and then marshalling them into JSON using a library like Jackson. To support this we’ve introduced the HttpBodyConverter
interface
public interface HttpBodyConverter {
String body();
String contentType();
}
It comes with a JSON implementation allowing you to do:
.post("/path").body(json(someObjectToMarshall))
.willReturn(success(json(otherObjectToMarshall, customObjectMapper))
Behind the scenes, your object will be marshalled into JSON with a Jackson ObjectMapper
, and the Content-Type
header will be set to application/json
.
You can easily create your own HttpBodyConverter
, as all you need to do is implement the interface for whatever Content-Type
you want. XML is currently on the roadmap, and more can follow.
Comparison with Java Alternatives
Betamax is similar to Hoverfly Java in the sense that it is a forward proxy which can be used to record and playback API simulations. However, whilst it is a good tool, it does not provide a DSL.
Two other great libraries which do have DSLs are MockRestServiceServer and Wiremock.
The main advantage of MockRestServiceServer is that it reduces the overhead of spinning up an actual web server, and instead works with RestTemplate
in order to return mock responses. This means that no HTTP traffic takes place, which can speed things up a fair amount. However, this tight coupling to Spring testing means that if you’re using something other than RestTemplate
you can’t use it (although I’ll take it over a clunky JAX-RS client).
Besides, when using Hoverfly Java there is automatic unused port selection, a shared instance across tests, and a startup time of less than one second. Because of this I don’t see an issue (or real overhead) with using real HTTP. HTTP will be used in production, so why not test it as early on as possible?
Wiremock is also great tool and like Hoverfly Java, it uses real HTTP. The main difference is that it acts as a web server as opposed to a proxy, meaning you need to point your client to localhost
instead of the real URL. Making a property file change may not be a huge issue, but as the proxy configuration can be inferred it removes any configuration boilerplate; all you need to do is instantiate Hoverfly.
You also have to consider that because Wiremock is a web server (or a virtual reverse-proxy more accurately), you need one Wiremock instance per stubbed API. This means as you add more services, startup time will increase, and so will the complexity of your configuration.
SSL is also slightly easier to set-up, as the self-signed certificate for Hoverfly is automatically trusted at runtime, meaning you don’t have to add it to the keystore yourself.
Other Trade-offs
There are other trade-offs, of course. Hoverfly uses Golang regex, which can be a confusing if you’re used to the standard Java regex in Wiremock. That’s why we’re planning to build a matcher interface to act as an abstraction around it (who enjoys regex anyway, really?).
Another missing feature is request verification, something that is currently on the roadmap for Hoverfly, but is already well-supported by Wiremock. Finally, the proxy design is not a magic bullet: there may be some (rare) cases where it’s difficult to get your code to work with a proxy. There are other differences as well, so please check out all the options if you are evaluating which one to use.
Conclusion
The Hoverfly Java DSL provides a really quick way of setting up a stub API or microservice. The proxy approach taken by Hoverfly means that you don’t really need to do anything other than declare a rule at the top of your test.
And remember that this is just a Java binding for Hoverfly. We have recently released a Python binding for Hoverfly and a JavaScript binding is on the roadmap. Hoverfly itself is a single binary that can be deployed easily on any OS and managed through its CLI. The aim is to make it easy to use Hoverfly in different environments throughout the software delivery pipeline.
If you have questions or suggestions on how we could improve Hoverfly Java, please use the comments below or raise an issue on the GitHub repository.