Vladimir Dyuzhev, author of MockMotor

Vladimir Dyuzhev
MockMotor Creator

Record Traffic as Mock Accounts

Traffic can be recorded as mock accounts to minimize the number of created mock responses.

Tweet This

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 and DestZip 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 196ms
  • 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.

Please Share

Was this post useful? Then please share! Tweet This