Vladimir Dyuzhev
MockMotor Creator
How to Record HTTP/JSON Traffic with MockMotor
MockMotor can act as a MITM to record and mock the traffic.
MockMotor saves you from tedious manual work.
One of the ways it does that is the automatic creation of mocks for passing traffic.
What is Forward Functionality?
A forward mock response is not producing a reply payload on its own. Instead, it forwards the request to an external URL, i.e. to a real backend.
In the request flow, MockMotor forwards URI, parameters, payload and headers down to the backend. That makes most authentications schemes (e.g. Basic HTTP or JWT) work without any special configuration.
In the response flow, MockMotor sends the response payload and response headers provided by the backend, to the caller.
Why Forwarding?
Forwarding greatly enlarges what you can do to test your applications while reducing the required efforts. Essentially, you only need to spend time mocking the responses you're focusing on, and you leave the rest to the live implementation.
- You can mock only selected few operations, relegating the rest to the real backend.
- You can modify the responses before sending them back to the consumer (e.g. adding new fields).
- You can record the traffic for later use as a pure mock service (i.e. without forwarding).
Let's see how we can do the recording.
Record Functionality
Forward responses, by default, only send the traffic to a live backend.
However, when the Record
is enabled, the response is not only sent back to the consumer but is also recorded as a fully configured mock.
Here the request from the client is forwarded to the Yandex API. When the reply is received, it is saved as a mock response that matches the original request, and then the response is sent to the client. Next time a request with the same parameters comes, it is served from the recorded mock response and is not forwarded to live Yandex API.
The recorded mock uses as many of the request parameters as possible to provide the exact match: SOAPAction or first element's name, Relative URI, parameters in the URI, names and values of HTTP query parameters. The mock also saves the response values: HTTP Status, payload, response time, content-type.
Recording Yandex Weather API
The Yandex Weather API is widely used by applications that deal with weather in Russia and its neighbours.
Its input is an HTTP GET request with two query parameters: lat
for latitude and lon
for longitude:
https://api.weather.yandex.ru/v1/forecast?lat=59.939015&lon=30.315804
The response contains a massive amount of information about the weather in the requested geographical point, in JSON format:
{
"now_dt": "2020-08-03T18:49:09.355Z",
"fact": {
"polar": false,
"accum_prec": {
"1": 3.7,
"3": 4.2,
"7": 23.3
},
"temp": 19,
"icon": "ovc",
"pressure_mm": 756,
"wind_dir": "w",
"source": "station",
"feels_like": 21,
"wind_gust": 6.3,
"condition": "overcast",
"temp_water": 16,
"uv_index": 0,
"pressure_pa": 1008,
"humidity": 87,
"season": "summer",
"wind_speed": 1,
"soil_moisture": 0.2,
"daytime": "n",
"soil_temp": 18,
"obs_time": 1596479524
},
"now": 1596480549,
"info": {
"nr": true,
"ns": true,
"f": true,
"def_pressure_mm": 759,
"_h": false,
"lon": 30.315804,
"nsr": true,
"n": true,
"tzinfo": {
...
I consider this an HTTP service and not a REST one. For a modern truly honestly REST service I'd expect the parameters to be in the URI segments, like this:https://api.weather.yandex.ru/v1/forecast/lat/59.939015/lon/30.315804
However, this distinction is debatable. We're not here to have an ivory-tower discussion, so let's just mock what we've got, ok?
Working Around the Limits
In the test mode, Yandex API only allows 5000 requests a day. It seems like a lot, but if you generate maps with weather markers, 5000 requests can all be used up before lunch.
The solution is, of course, to mock the responses.
The number of locations used in test scenarios is limited. We can mock them all and then execute tens of thousands of requests - without any scowl from Yandex.
Let's Be Lazy
Let's create the mocks the most effortless way - make MockMotor record the responses for us.
Create a Service
First, we need to create a test service that mocks the Yandex Weather API. I just did that:
The mock service's relative URI is /yandex-weather
.
Create a Forward Response
A forward mock response is a special mock response that contains no own response payload. Instead, it is configured to forward the request to the live backend.
How a forwarding mock should look?
- It should match any HTTP operation.
- It should use Javascript (because the response payload is JSON).
- It should have the Yandex API URL set in the Forward field.
- It should have
Record
enabled.
In the forwarding response, we shouldn't have any response payload, HTTP status, content-type, attachments or delay set. They are used for altering the response properties, so clear those fields.
Below is the forward response. Unrelated properties are hidden:
Note the Forward URL field that contains the target system hostname. Anything in the request path after the mock service URI is appended to the forward URL. That includes the relative path and query parameters.
E.g. a client calling the mock service with this URL:
https://127.0.0.1:20075/yandex-weather/v1/forecast?&lat=59.939015&lon=30.315804
MockMotor then removes the mock service URL (https://127.0.0.1:20075/yandex-weather
) to figure out the relative URL:
/v1/forecast?&lat=59.939015&lon=30.315804
Then MockMotor appends this relative URI to the forward URL (https://api.weather.yandex.ru
), and calls Yandex with this:
https://api.weather.yandex.ru/v1/forecast?lat=59.939015&lon=30.315804
At this point, the brand new service has the only response - the forwarding one:
Execute a Request for Weather
Now, let's execute our first request.
Since we do not have any local mocks yet, the request will be handled by the forwarding mock. It sends the request to the actual Yandex backend. When Yandex responds, MockMotor records the response properties as a new mock.
Let's find the weather in St. Petersburg. This city is my childhood home, and also a home of Russian Tsars, the birthplace of the 1917 revolution and the location of lots of other interesting things, but it is often rainy there, so it worth checking.
The latitude and longitude of St. Petersburg is 59.939012,30.315804. (To be precise, this is a location of the Palace Square - the very heart of SPb).
GET http://127.0.0.1:7080/ExamplesForBlog/yandex-weather/v1/forecast?&lat=59.939012&lon=30.315804 HTTP/1.1
Accept-Encoding: gzip,deflate
X-Yandex-API-Key: 3ae58b34-....-....-....-da111b3d6fe7
Host: 127.0.0.1:7080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Oh great, we've got a response! Lots of data in it (2 degrees Celsius - cold, as usual):
{
"now": 1553914074,
"now_dt": "2019-03-30T02:47:54.740Z",
"info": {
"f": true,
"n": true,
"nr": true,
"ns": true,
"nsr": true,
"p": true,
"lat": 59.939012,
"lon": 30.315804,
"tzinfo": {
"name": "Europe/Moscow",
"abbr": "MSK",
"offset": 10800,
"dst": false
},
"def_pressure_mm": 759,
"def_pressure_pa": 1012,
"_h": false,
"url": "https://yandex.ru/pogoda/?lat=59.939012&lon=30.315804"
},
"fact": {
"temp": 2,
"feels_like": -2,
"temp_water": 0,
...
Let's check what MockMotor recorded.
Review the Recorded Response
Let's take a look at the service's responses again.
You can see the newly recorded mock. It is placed ahead of the forward mock, so any new matched traffic is handled by mock.
That is, if we have another request with the same lat
and lon
, it is responded by the mock, and not the live backend.
You can notice that MockMotor recorded:
- HTTP operation
GET
- Relative URI
/v1/forecast
- Query parameter
lon
value - Query parameter
lat
value - Response time
564
ms
Looking inside of the recorded response, you can see it has recorded the response payload, HTTP status and content type as well:
Test It
Let's test our new mock. We execute the same request for Palace Square once again. Now it should be replied from mock, and don't eat into our daily call limit.
And indeed, you can see X-MockMotor-Delay: 564
header in the response, telling us that the response was generated locally.
You can also see that the call count next to the mock response has incremented:
Repeat
To add more locations, just call the mock service with more combinations of lat
and lon
.
After running for some time, the mock service handles any predefined test locations from your test scenarios, not leaking any calls to the actual Yandex service.