This is the second article of a two-part series comparing Prism, Hoverfly, and HMT. This article focuses on using all three tools to build a mock of the github API v3, whereas the first article focuses on a high level comparison of the tools.
Team HMT busy building HMT.
In the previous article, we saw how to create a Prism server from an OpenAPI spec. We also saw how Hoverfly can play back recordings of server traffic. Lastly, we saw how HMT blends OpenAPI specs and recordings into a mock server.
In this article, we will examine how all three tools can mock the Stripe API.
"title":"Your schema contains $ref. You must provide specification in the third parameter.",
"status":500,
"detail":""
}
While Prism is good at mocking most API specs, it struggled when it ran into the Stripe spec. I'm sure the team will fix this in an upcoming version of Prism. In the meantime, let's see how Prism handles the ubiquitous petstore.yml spec.
$ prism mock petstore.yml
Now, when we query /pets, we get the following result.
[
{
"id":-9223372036854776000,
"properties":{
"isCat":true
},
"name":"string",
"tag":"string"
}
]
Conclusion: Prism will create an out-of-the-box mock for many APIs. Although it struggled with Stripe, it works for most OpenAPI specs. It also has helpful logs.
To create a Hoverfly mock, we first have to record the calls to the API we would like to mock. I'll use my Stripe test account, where I've created three mock customers. For example, here is the information for a customer named Jane Doe.
If we call the Stripe /v1/customers endpoint, we get information on all three mock customers.
Now, let's fire up Hoverfly and record our interactions with some Stripe endpoints. First, we'll need to get the Hoverfly SSL certificate so we can call the Stripe HTTPS endpoint.
$ curl --proxy localhost:8500 -u **redacted** -X DELETE https://api.stripe.com/v1/customers --cacert cert.pem # try to delete all customers, will fail!
$ curl --proxy localhost:8500 -u **redacted** -X DELETE https://api.stripe.com/v1/customers/cus_GhmV4PbooARHhM --cacert cert.pem # try to delete one customer, will succeed
$ curl --proxy localhost:8500 -u -X DELETE https://api.stripe.com/v1/customers/cus_GhmV4PbooARHhM --cacert cert.pem # leave out the key, will fail
$ curl --proxy localhost:8500 -u **redacted** -X DELETE https://api.stripe.com/v1/customers/cus_GhmV4PbooARHhM # delete a customer that doesn't exist, will fail
$ hoverctl export stripe-simulation.json
$ hoverctl stop
Digressing for a moment, I'd like to say a word about making recordings for tests. As you saw in the example above, POST or DELETE mutates the real data backing your requests. If you're not careful, this can lead to some pretty bad consequences. That's why I would always recommend recording real server traffic.
Back to Hoverfly, let's now examine how our recordings are transformed into a mock API. We'll start by firing up a Hoverfly server.
Hoverfly produces a helpful error message indicating what went wrong. It also gives suggestions how to fix the matcher to produce a result. Here is a truncated error message:
Hoverfly Error!
There was an error when matching
Got error: Could not find a match for request, create or record a valid matcher first!
The following request was made, but was not matched by Hoverfly:
{
"Path":"/v1/customers/foobar",
"Method":"GET",
...
}
Whilst Hoverfly has the following state:
{}
But it did not match on the following fields:
[path]
Which if hit would have given the following response:
{
"status":200,
"body":"...",
}
Let's follow its suggestion and add a glob wildcard to stripe-simulation.json. Before, the file contained this.
[
{
"matcher":"exact",
"value":"/v1/customers/cus_GhmV4PbooARHhM"
}
]
And now, we will change it to this.
[
{
"matcher":"glob",
"value":"/v1/customers/*"
}
]
Running our curl request again, we get the recorded response. w00t! Here's a shortened version of the response.
{
"id":"cus_GhmV4PbooARHhM",
"object":"customer",
"account_balance":0,
"address":{
"city":"Melbourne",
"country":"AU",
"line1":"1 Batman Way",
"line2":"",
"postal_code":"3000",
"state":"NSW"
},
...
"tax_info":null,
"tax_info_verification":null
}
This is the basic flow in Hoverfly. For small APIs, I find this to be a compelling approach. For larger APIs with more complex behavior, OpenAPI-based mocks tend to perform better. This is because the spec will likely contain more outcomes and endpoints than you would see in the wild.
We'll create a folder called stripe-mock, where we will move the spec.
$ mkdir stripe-spec &&mv spec3.yaml ./stripe-spec
Already here, we can start HMT in server mode an examine some results.
$ hmt mock -a ./stripe-spec
$ curl -i -X GET http://localhost:8000/v1/clients
We can see in the gist below that the result is an array of mock users built using Stripe's OpenAPI spec.
{
"data":[
{
"created":74675021,
"id":"dolor enim minim culpa ipsum",
"livemode":true,
"object":"customer",
...
}
...
],
"has_more":true,
"object":"list",
"url":"/v1/customers"
}
Now, let's enrich the spec with the same set of recordings we made when building our Hoverfly mock. Blending an OpenAPI spec with recorded server traffic has two main advantages:
it allows HMT to pick up on features of the spec not present in the recordings and vice versa.
you can alternate between serving rote recordings and synthetic data.
For example, let's record a call to /v1/customers as we did in the previous example.
$ curl http://localhost:8000/https/api.stripe.com/v1/customers # leave out the key
$ curl -u **redacted** http://localhost:8000/https/api.stripe.com/v1/customers # include the key
$ curl -u **redacted** http://localhost:8000/https/api.stripe.com/v1/customers/cus_GhmV4PbooARHhM --cacert cert.pem # getting one customer # get one customer
$ curl -u **redacted** -X DELETE http://localhost:8000/https/api.stripe.com/v1/customers --cacert cert.pem # try to delete all customers, will fail!
$ curl -u **redacted** -X DELETE http://localhost:8000/https/api.stripe.com/v1/customers/cus_GhmV4PbooARHhM --cacert cert.pem # try to delete one customer, will succeed
$ curl -u -X DELETE http://localhost:8000/https/api.stripe.com/v1/customers/cus_GhmV4PbooARHhM --cacert cert.pem # leave out the key, will fail
$ curl -u **redacted** -X DELETE http://localhost:8000/https/api.stripe.com/v1/customers/cus_GhmV4PbooARHhM # delete a customer that doesn't exist, will fail
Now, let's build a spec using this mock. We'll use gen mode. That means that if our mock runs into a customer it hasn't recorded, it will create a mock customer on the fly.
In this article, we saw how three mocking tools - Prism, Hoverfly and HMT - can create a mock of the Stripe API. I hope that, in going through this, you got a sense of the differences between the three tools. In short:
Prism can create a mock of an OpenAPI spec
Hoverfly can serve back recordings
HMT can serve a mix of recordings and synthetic data