Hoverfly is a tool specifically designed for simulating and controlling HTTP and HTTPS interactions. It allows developers to minimize dependencies on external services during testing by providing realistic simulations of these services. In this techup, we will look at the basic topics and show how to use Hoverfly in a Java project including GitHub actions.
Simulating HTTP requests
The central component of the library is the Hoverfly class, which is responsible for abstracting and controlling a Hoverfly instance. Here is a typical flow:
|
|
Available operating modes of Hoverfly
Before we look at a practical example step by step, I would like to briefly explain the different operating modes available to cover different testing requirements and make optimal use of API simulation.
Simulation mode (Simulating)
In simulation mode, Hoverfly acts as if it were the real service and responds to requests accordingly. This is particularly useful for avoiding dependencies on external services during testing. Later we will show a concrete example of how to set up this mode.
|
|
Proxy mode (Spy mode)
Hoverfly’s SPY mode allows HTTP requests to be forwarded to a real API if no matching simulation exists for those requests. Hoverfly acts as a proxy that monitors and records network traffic. When a request is made, Hoverfly first checks to see if there is a simulation for that request. If so, the mocked version is returned. If not, the request is forwarded to the real API.
|
|
[!NOTE]
You can find a detailed example in our Techup repository
Recording API requests (Capture mode)
Hoverfly can also be operated in capture mode, where network traffic to a real API can be recorded and the received responses can be saved. For this, the exportSimulation
method must be defined. The saved JSONs can then even be used in simulation mode in a further step. With this mode, the desired request can be recreated very precisely.
|
|
[!NOTE]
You can find a detailed example in our Techup repository
Recognizing differences: The Diff mode
In Diff mode, Hoverfly detects the differences between a simulation and the actual requests and responses. Hoverfly then creates a diff report that you can review later.
|
|
Advanced features of Hoverfly
In addition to the basic operating modes, Hoverfly offers a number of advanced features that allow you to simulate more complex scenarios and significantly increase the flexibility of your API tests.
Defining API endpoints: Using the Domain Specific Language (DSL)
Hoverfly provides a DSL to define requests and responses in Java instead of JSON. This allows for a fluent and hierarchical definition of endpoints.
|
|
Simulating network latency
It is possible to simulate network latency, either globally for all requests or specifically for certain HTTP methods.
|
|
Matching requests precisely: Request Field Matchers
Hoverfly provides matchers to define complex requests, such as wildcards, regex, or JSON path matching.
|
|
State-based simulations (Stateful Simulation)
Hoverfly supports stateful simulations where a service returns different responses based on the current state.
|
|
Verifying requests: The Verification function
The verification function can be used to check whether certain requests were made to the external service endpoints.
|
|
Hands-On - Testing a REST client in Java with Hoverfly
In this part, we will walk through step-by-step how to implement a REST client in Java with different API endpoints. This example uses the MicroProfile Rest Client and shows how to use Hoverfly to simulate and test this interaction.
Installing the required dependencies
In our case, we have a Quarkus application with maven. So we simply add the following dependencies to our pom.xml:
|
|
Creating and configuring the REST client
First, we define an interface called BnovaRestClient
. With the @Path
annotation, we define the base path for this endpoint, in this case /techhub
.
For registering the client, the @RegisterRestClient
annotation is used. This annotation allows the interface to be injected and used later within our controller.
The first method we create is getById
. We set the @GET
annotation, which means it is an HTTP GET
request. The @Produces
annotation indicates that the method should return a JSON response. This method takes a query parameter id
to retrieve the Techup object based on the passed ID.
|
|
To use the REST client in our application, we need a controller that can be called by the application. The controller is used as a mediator between the requests made to our application and the REST client we defined earlier.
Here is the code for the TechupController
:
|
|
The TechupController
is a REST controller that handles requests to the /techhub
path, which is defined by the @Path
annotation.
The @RestClient
annotation injects the BnovaRestClient
into the controller. This allows the controller to use the REST client to make external requests.
The getTechupById
method is annotated with @GET
, marking it as an HTTP GET request. The specific path for this method is /{id}
, where id
is a path parameter. The @Produces
annotation indicates that the method returns a JSON response.
Setting up and running tests
Now that we have implemented the REST client and the controller, the next step is to create tests to ensure that everything is working as expected.
Creating the test class: TechupTest
First, we create the test class TechupTest
. For this, we need two annotations: @QuarkusTest
, which marks the class as a Quarkus test so that the Quarkus test framework initializes the environment, and @QuarkusTestResource(value = HoverflyResource.class)
, which links the test to the HoverflyResource
. We will create this resource in the next step as a test resource and use Hoverfly to simulate the API calls.
For all subsequent tests, the RestAssured library is used to send an HTTP GET request to the corresponding endpoint. In the testGetById
test, this is the /techhub/{id}
path. The given()
method sets the path parameter id
to 1
. Then the request is executed with when()
and get("/techhub/{id}")
.
The subsequent then()
chain checks whether the HTTP status code of the response is 200 (OK)
, whether the content type of the response is application/json
and whether the fields of the response correspond to the expected values.
|
|
Creating a Hoverfly resource for mocking
Now we also need to create the HoverflyResource
that we already referenced in the test class. This class serves as our mock environment for our BnovaRestClient
.
The class HoverflyResource
implements the interface QuarkusTestResourceLifecycleManager
, which provides methods for managing the lifecycle of the test resource.
In the start()
method, a Hoverfly instance is first started with the constant SIMULATE
in simulation mode so that we can simulate what the API responses should look like. However, the first parameter calls the localConfigs()
method, which returns a configuration for running Hoverfly locally. This configuration contains various default settings. The only thing we adjust here is the target URL that Hoverfly should simulate. We used the constant SERVICE_URL
, which has “my-hoverfly-service
” as its value. This way we can be sure that only requests to this specific URL are intercepted and simulated by Hoverfly. To make this work later via the CLI and also within the Github actions, we also set this value via the application.properties
:
|
|
To start the actual simulation mode, the .simulate()
method is used. The individual mocks can then be defined as parameters. The best way to do this is to use the Domain Specific Language so that it is clearly structured and readable. To do this, you simply need to use the .dsl()
method.
The following example specifies that the simulation should respond to GET requests to the /techhub
path if there is a parameter id
with the value 1
. If this is the case, the content from the JSON file example_get_by_id.json
should be returned.
|
|
The response is a Techup object in JSON format:
|
|
Integrating Hoverfly into GitHub Actions
If, for example, we now want to build our application using GitHub Action, a mvn clean install
is required. This will also run all tests. And of course, our Hoverfly mock must also run here. And here comes the big surprise, there is nothing more to do than start a build. Here is my example GitHub Action:
|
|
As soon as I push to main, the action starts. You can see in the log that it was executed successfully:
|
|
Conclusion
Hoverfly is an extremely versatile and powerful tool specifically designed for simulating API interactions in microservice environments. With its various operating modes, it offers a comprehensive solution to eliminate dependencies on external services during testing, record and simulate real API requests, and detect differences between simulations and actual responses. Thanks to its seamless integration into Java projects and CI/CD pipelines, Hoverfly enables continuous and automated testing of applications, significantly increasing reliability and efficiency in the development process. Developers who need realistic and flexible API simulations will find Hoverfly to be a suitable tool.
Hoverfly has already been successfully integrated into various customer projects!
[!TIP]
All examples are also available in our Techhub Repository
This techup has been translated automatically by Gemini