Vladimir Dyuzhev
MockMotor Creator
Record Traffic as Mock Accounts
Traffic can be recorded as mock accounts to minimize the number of created mock responses.
In How to Record SOAP Traffic, I have shown the recording of 1600 different responses.
However, there is a problem.
The response time of the real backend service used for the post is about 100ms. MockMotor tries to simulate the real service's behaviour, including its response time (RT), but with so many created responses, it is not easy.
To find a matching response, MockMotor has to scan thru the list of generated responses until it finds the one that matches. That scanning takes time - a very little time, but it adds up. For responses at the end of the list, the actual response time is not 100ms, but closer to 300ms.
This is not a problem for functional testing (DEV, DIT, SFT/UAT). However, it becomes a problem for load testing (LT). In LT, any response time deviation is a reason for the investigation and a potential blocker for the release.
There is a way to resolve this. We need to do the recording the other way - have only one response, but multiple mock accounts.
Let's do this.
Account Properties
We're going to have one mock response and many mock accounts. That means that the response payload must be parameterized with the account values.
In our case (Averitt TransiteTime), the important response values are OrigZip
, OrigState
, DestZip
, DestState
, EstimatedDays
and Comment
(which contains the status for some
responses).
The OrigZip/OrigState
and DestZip/DestState
are coming in the request, and EstimatedDays
and Comment
are from the response.
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tran="http://webservices.averittexpress.com/TransitTimeService">
<soapenv:Header />
<soapenv:Body>
<tran:getTransitTime>
<arg0>
<tran:OriginCity>Seattle</tran:OriginCity>
<tran:OriginState>WA</tran:OriginState>
<tran:OriginZip>98103</tran:OriginZip>
<tran:DestinationCity>Houston</tran:DestinationCity>
<tran:DestinationState>TX</tran:DestinationState>
<tran:DestinationZip>77092</tran:DestinationZip>
</arg0>
</tran:getTransitTime>
</soapenv:Body>
</soapenv:Envelope>
The desired mock response payload may look like below.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns2:getTransitTimeResponse xmlns:ns2="http://webservices.averittexpress.com/TransitTimeService">
<return>
<ns2:OrigCity>Seattle</ns2:OrigCity>
<ns2:OrigState>WA</ns2:OrigState>
<ns2:OrigZip>98103</ns2:OrigZip>
<ns2:origServiceCenter>NORTH AMERICA LTL NETWORK</ns2:origServiceCenter>
<ns2:DestCity>Houston</ns2:DestCity>
<ns2:DestState>TX</ns2:DestState>
<ns2:DestZip>77092</ns2:DestZip>
<ns2:destServiceCenter>HOUSTON SERVICE CENTER</ns2:destServiceCenter>
<ns2:directShipment>no</ns2:directShipment>
<ns2:EstimatedDays>5</ns2:EstimatedDays>
<ns2:EstimatedDeliveryDate>08/19/2020</ns2:EstimatedDeliveryDate>
<ns2:Comment/>
</return>
</ns2:getTransitTimeResponse>
</soapenv:Body>
</soapenv:Envelope>
The response values should be updated with the $account
values, e.g.:
...
<ns2:OrigZip>{$account//*:originZip/text()}</ns2:OrigZip>
...
Strictly speaking, the *City
values should be recorded as account properties as well, but the client application does not use those, and so they are ignored.
Creating Account Properties
Before using account properties in response, we need to create those properties.
Let's add one property per value that we need to update in the response:
Setting Up the Forwarding Mock
The requests, when an account for them is not found yet, will be handled by the forwarding mock.
- The forwarding mock sends each request to the actual Averitt backend first.
- It builds match conditions for the request and checks if a mock response with the same match conditions already exists.
- If it doesn't, MockMotor creates one and parametrizes its payload with the variables defined in the
Update Mock Account
. - Finally, MockMotor records the mock account with the values from the request and the response.
The forwarding mock is mostly the same as the one used in the previous post.
The new configuration is the account matching section. The forwarding response tries to find an account with the OriginZip
and DestinationZip
values that match the ones from the
request.
If the account is not found, the forwarding mock creates this account, and populates its originZip
and destinationZip
properties
with the values from the request:
We also update the account with the properties that are coming from the response. To update them, we use the Update Account
section:
Response Payload Parametrization
The originZip
and destinationZip
account properties are updated in the Update Accounts
section again, even though they were
already read in the Account Selection
section. Why?
This is because MockMotor uses the XPath (or JS path) from the Update Accounts
section for finding the position in the response payload
where the static value should be replaced with the account property value (i.e. parametrized).
If the *Zip
properties were not defined in this section, the <DestZip>
and <OrigZip>
values in the recorded mock response would be hardcoded.
Example: the OrigState
and OrigZip
are scripted, while OrigCity
and other properties are not.
<ns2:getTransitTimeResponse xmlns:ns2="http://webservices.averittexpress.com/TransitTimeService">
<return>
<ns2:OrigCity>Seattle</ns2:OrigCity>
<ns2:OrigState>{$account//*:originState/text()}</ns2:OrigState>
<ns2:OrigZip>{$account//*:originZip/text()}</ns2:OrigZip>
<ns2:origServiceCenter>NORTH AMERICA LTL NETWORK</ns2:origServiceCenter>
<ns2:DestCity>Houston</ns2:DestCity>
...
The Complete Forwarding Mock
To summarize, the forwarding mock:
- Matches
POST
HTTP operation as befits SOAP. - Matches any SOAP operation.
- Uses XQuery scripting because the requests are XML.
- Uses
OrigZip
andDestZip
from the request to select an account. - Creates a mock account if not found.
- Forward the request to the Averitt service's URL.
- Has
Recording
enabled and creates a mock response if none yet. - Updates the account properties from the response data.
Below is the complete forwarding mock. Unused properties are hidden.
Ready to Use
Here is the forwarding mock in the responses list - still the only response there:
Execute 1600 Requests
Now, let's execute the requests for each pair of 40 ZIP codes that were used in the previous post.
AL,Huntsville,35801
AK,Anchorage,99501
AZ,Phoenix,85001
...
I execute those requests with a script which you can also find in the previous post.
Review the Recorded Response and Accounts
Single Mock Response
Comparing to the “one mock per account” used in the previous post, now we only create one mock response. However, we create one mock account per request.
The singular mock response is parametrized with the values from the mock accounts.
In short, instead of 1600 mock responses, we have 1600 mock accounts.
Let's take a look at the service responses again:
The forwarding mock was called 1600 times. There is a newly recorded mock before it, but the recorded mock itself got no traffic yet - all traffic so far is handled by the forwarding mock and then the live service.
The getTransitTime
mock response requires a mock account. If no account is matching yet, the match script that uses $account
is false
,
and MockMotor skips the response and executes the next response in the list, i.e. the forwarding mock.
In addition to the response payload and the match script, MockMotor recorded:
- HTTP operation
POST
- Top payload element
getTransitTime
(SOAPAction is not defined for this operation) - HTTP status
200
- Content-Type
text/xml;charset=utf-8
- Response time
196
ms - The request to use when clicking the
Debug
button.
Mock Accounts
If we look into the environment accounts tab, we see the recorded accounts. There are 1600 of them, as expected.
Here I downloaded them as a spreadsheet for readability:
Test It
Let's test the new mock.
I execute the same script as before. Now it should be replied by the generated mock and do not call the Averitt backend.
And, indeed, the call count next to the recorded mock is incrementing (to 1600), showing that all the responses are served from the mock accounts.
You can see the complete service on the demo MockMotor instance.