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.

To resolve this, there is a way 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. The values are updated with the $account values.

Note that we do not create this response manually - it is recorded.

<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>{$account//*:originState/test()}</ns2:OrigState>
            <ns2:OrigZip>{$account//*:originZip/test()}</ns2:OrigZip>
            <ns2:origServiceCenter>NORTH AMERICA LTL NETWORK</ns2:origServiceCenter>
            <ns2:DestCity>Houston</ns2:DestCity>
            <ns2:DestState>{$account//*:destinationState/test()}</ns2:DestState>
            <ns2:DestZip>{$account//*:destinationZip/test()}</ns2:DestZip>
            <ns2:destServiceCenter>HOUSTON SERVICE CENTER</ns2:destServiceCenter>
            <ns2:directShipment>no</ns2:directShipment>
            <ns2:EstimatedDays>{$account//*:estimatedDays/text()}</ns2:EstimatedDays>
            <ns2:EstimatedDeliveryDate>04/29/2019</ns2:EstimatedDeliveryDate>
            <ns2:Comment>{$account//*:deliveryComment/text()}</ns2:Comment>
         </return>
      </ns2:getTransitTimeResponse>
   </soapenv:Body>
</soapenv:Envelope>

Strictly speaking, the *City values should be updated into account 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 a Forwarding Mock

The forwarding mock is generally the same as the one used in the previous post.

The new configuration is the account matching section. The response tries to find an account with the OriginZip and DestinationZip 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:

That takes care of 2 properties. To update the other 4 in the account, we need to wait until we get the response from the real backend and then use the Update Account section:

The OrigState and DestState could have been read from the request as well. Since these are the same values as in response, this doesn’t make any difference.

Recording Keys

Unlike the previous post, the recording keys are not required. We’re only having one response per operation, and the selected account already contains the key values (originZip and destinationZip).

If we keep them, nothing changes - we’re just going to perform an unnecessary check during the execution.

Tying it All Together

For the benefit of those who want to see all the steps, let’s repeat them:

Create a Service

First, we need to create a test service that is going to mock the Averitt API:

Create a Forward Response

How to configure the forwarding mock?

  • It should match POST HTTP operation as befits SOAP.
  • It should match any SOAP operation.
  • It should use XQuery scripting because the requests are XML.
  • It should use OriginZip and DestinationZip from the request to select an account.
  • It should create a mock account if not found.
  • It should have the Averitt service’s URL set in the Forward field.
  • It should have Recording enabled.
  • It should activate the recorded mock immediately.
  • Below is the forward response:

    Response Payload Parametrization

    The originState and destinationState 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 value should be replaced with the account property value (i.e. parametrized).

    If the *State properties were not defined in this section, the <DestState> and <OrigState> values in the response would be hardcoded.

    Ready to Use

    Here is the forwarding mock in the responses list - still alone:

    Execute 1600 Requests

    Now, let’s execute the requests for each pair of ZIP codes.

    Since I do not have any local mocks yet, the requests will be handled by the forwarding mock. It sends each request to the actual Averitt backend first. When the backend responds, MockMotor records the response and its properties as a new mock.

    Any request with the same recording keys as seen by the service before is handled by one of the previously generated mocks.

    I execute those requests with a script which you can find in the previous post.

    Review the Recorded Responses

    Let’s take a look at the service responses again:

    The forwarding mock was called 1600 times, and there is a recorded mock above it, but the recorded mock itself got no traffic yet - all traffic so far is handled by 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.
  • 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, you can see X-MockMotor-Delay header in the responses, telling that the response was generated locally:

    HTTP/1.1 200 OK
    Connection: keep-alive
    Date: Mon, 16 May 2019 07:34:11 GMT
    X-MockMotor: 1.8.2288
    Content-Type: text/xml;charset=utf-8
    X-MockMotor-Delay: 122
    
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
       <soapenv:Body>
          <ns2:getTransitTimeResponse xmlns:ns2="http://webservices.averittexpress.com/TransitTimeService">
             <return>
                <ns2:OrigCity>Huntsville</ns2:OrigCity>
                <ns2:OrigState>AL</ns2:OrigState>
                <ns2:OrigZip>35801</ns2:OrigZip>
                <ns2:origServiceCenter>DECATUR SERVICE CENTER</ns2:origServiceCenter>
                <ns2:DestCity>Phoenix</ns2:DestCity>
                <ns2:DestState>AZ</ns2:DestState>
                <ns2:DestZip>85001</ns2:DestZip>
                <ns2:destServiceCenter>NORTH AMERICA LTL NETWORK</ns2:destServiceCenter>
                <ns2:directShipment>no</ns2:directShipment>
                <ns2:EstimatedDays>4</ns2:EstimatedDays>
                <ns2:EstimatedDeliveryDate>05/20/2019</ns2:EstimatedDeliveryDate>
                <ns2:Comment/>
             </return>
          </ns2:getTransitTimeResponse>
       </soapenv:Body>
    

    The call count next to the recorded mock is incrementing too (to 1600), while the one next to forwarding response stays the same.

    You can see the complete service on the demo MockMotor instance.

    Please Share

    Was this post useful? Then please share! Tweet This