Vert.X meets Service Virtualization with Hoverfly
Service Virtualization is a technique using to emulate the behaviour of dependencies of component-based applications.
Hoverfly is a service virtualisation tool written in Go which allows you to emulate HTTP(S) services. It is a proxy which responds to HTTP(S) requests with stored responses, pretending to be it’s real counterpart.
Hoverfly Java is a wrapper around Hoverfly, that let’s you use it in Java world. It provides a native Java DSL to write expectations and a JUnit rule to use it together with JUnit.
But apart from being able to program expectations, you can also use Hoverfly to capture traffic between both services (in both cases are real services) and persist it.
Then in next runs Hoverfly will use these persisted scripts to emulate traffic and not touch the remote service. In this way, instead of programming expectations, which means that you are programming how you understand the system, you are using real communication data.
This can be summarised in next figures:
First time the output traffic is sent though Hoverfly proxy, it is redirected to real service and it generates a response. Then when the response arrives to proxy, both request and response are stored, and the real response is sent back to caller.
Then in next calls of same method:
The output traffic of Service A is still sent though Hoverfly proxy, but now the response is returned from previous stored responses instead of redirecting to real service.
So, how to connect from HTTP client of Service A to Hoverfly proxy? The quick answer is nothing.
Hoverfly just overrides Java network system properties (https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html) so you don’t need to do anything, all communications (independently of the host you put there) from HTTP client will go through Hoverfly proxy.
The problem is what’s happening if the API you are using as HTTP client does not honor these system properties? Then obviously all outgoing communications will not pass thorough proxy.
One example is Vert.X and its HTTP client io.vertx.rxjava.ext.web.client.WebClient. Since WebClient does not honor these properties, you need to configure the client properly in order to use
Hoverfly.
The basic step you need to do is just configure WebClient with proxy options that are set as system properties.
final Optional<ProxyOptions> proxyOptions = useSystemPropertiesProxyOptions(); if (proxyOptions.isPresent()) { final WebClientOptions webClientOptions = new WebClientOptions(); webClientOptions.setProxyOptions(proxyOptions.get()); client = WebClient.create(vertx, webClientOptions); } else { client = WebClient.create(vertx); } private Optional<ProxyOptions> useSystemPropertiesProxyOptions() { final String httpsProxy = System.getProperty("https.proxyHost"); if (httpsProxy != null) { return Optional.of(new ProxyOptions() .setHost(httpsProxy) .setPort(Integer.parseInt(System.getProperty("https.proxyPort", "443")))); } else { final String httpProxy = System.getProperty("http.proxyHost"); if (httpProxy != null) { return Optional.of(new ProxyOptions() .setHost(httpsProxy) .setPort(Integer.parseInt(System.getProperty("http.proxyPort", "80")))); } } return Optional.empty(); }
Notice that the only thing that it done here is just checking if system properties regarding network proxy has been configured (by Hoverfly Java) and if it is the case just create a Vert.X ProxyOptions object to configure the HTTP client.
With this change, you can write tests with Hoverfly and Vert.X without any problem
public class VillainsVerticleTest { private static Vertx vertx; private static String RESPONSE = "[\n" + " {\n" + " \"name\": \"Moon\",\n" + " \"villain\": \"Gru\",\n" + " \"wiki\": \"https://en.wikipedia.org/wiki/Moon\"\n" + " },\n" + " {\n" + " \"name\": \"Times Square JumboTron\",\n" + " \"villain\": \"Gru\",\n" + " \"wiki\": \"https://en.wikipedia.org/wiki/One_Times_Square\"\n" + " }\n" + "]"; @ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(dsl( service("crimes:9090") .get("/crimes/Gru") .willReturn(success(RESPONSE, "application/json")) )); @BeforeClass public static void deployVerticle() throws InterruptedException { final CountDownLatch waitVerticleDeployed = new CountDownLatch(1); new Thread(() -> { vertx = Vertx.vertx(); DeploymentOptions deploymentOptions = new DeploymentOptions(). setConfig(new JsonObject() .put("services.crimes.host", "crimes") .put("services.crimes.port", 9090)); vertx.deployVerticle(VillainsVerticle.class.getName(), deploymentOptions, event -> { if (event.failed()) { throw new IllegalStateException("Cannot deploy Villains Verticle"); } waitVerticleDeployed.countDown(); }); }).start(); waitVerticleDeployed.await(); } @Test public void should_get_villain_information() { given() .when() .get("villains/{villain}", "Gru") .then() .assertThat() .body("name", is("Gru")) .body("areaOfInfluence", is("Worldwide")) .body("crimes.name", hasItems("Moon", "Times Square JumboTron")); } }
In previous example Hoverfly is used as in simulate mode and the request/response definitions comes in form of DSL instead of an external JSON script. Notice that in this case you are programming that when a request by current service (VillainsVerticle), is done to host crimes and port 9090, using GET HTTP method at /crimes/Gru then the response is returned. For sake of simplicity of current post this method is enough.
You can see source code at https://github.com/arquillian-testing-microservices/villains-service and read about Hoverfly Java at http://hoverfly-java.readthedocs.io/en/latest/
Reference: | Vert.X meets Service Virtualization with Hoverfly from our JCG partner Alex Soto at the One Jar To Rule Them All blog. |