Skip to content

Examples and Case Studies

Example 1. Remote Token Introspection Service (Static Mock)

A company utilizes a self-hosted OAuth 2.0 server-to-server authorization infrastructure. One of the company's security paradigms is that every call between two services must be secured by OAuth 2.0 infrastructure. Every service must check if the OAuth token provided in the request is valid. This check is done using remote token introspection.

When an OAuth 2.0 client makes a request to the resource server, the resource server needs some way to verify the access token. The OAuth 2.0 core spec does not define a specific method of how the resource server should verify access tokens, and it just mentions that it requires coordination between the resource and authorization servers. The OAuth 2.0 Token Introspection extension defines a protocol that returns information about an access token, intended to be used by resource servers or other internal servers. The token introspection endpoint needs to be able to return information about a token.

The problem

The problem with real token introspection endpoints in the development environment is that it cannot handle high traffic, is unstable, and issuing token takes a significant amount of time. This results in flaky and long-running tests.

Example 1 - Real Introspection Service

The solution

For this reason, the team developing one of the services decided to simulate the development instance of the token introspection endpoint to stabilize and speed up automatic acceptance tests.

Example 1 - Mocked Introspection Service

The token introspection endpoint consumes the body in the form of token= and should return two types of responses:

  • success for a token with value ValidToken
  • error response for tokens with different values

Sample request:

POST /token_info HTTP/1.1

token=c1MGYwsDSdqwJiYmYxNDFkZjVkOGI0MSAgLQ

Sample response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "active": true,
  "scope": "read write email",
  "client_id": "J8NFmU4tJVdfasdfaFmXTWvaHO",
  "exp": 1437275311
}

Mock of success response

A mock returning a success response should match the following fields:

  • HTTP Method: POST
  • Path: /token_info
  • Body: equals "token=ValidToken"

The mock may return a fixed body with an HTTP status of 200 OK and Content-Type application/json, so a static mock is appropriate for the given use case (Note: exp field is set to a value far in the future).

{
  "active": true,
  "scope": "read write email",
  "client_id": "Client123",
  "exp": 2000000000
}

Mock of failed response

A mock returning a failed response should match the following fields:

  • HTTP Method: POST
  • Path: /token_info
  • Body: does not equal "token=ValidToken"

The mock returns a fixed body with HTTP status of 200 OK and Content-Type application/json.

{
  "error": "invalid_client",
  "error_description": "Some error message"
}

Example 2. SMS Sending Service (Template Mock)

One of the company products uses an external service called SMS Gateway. The service is used to send out SMS notifications to customers whenever the status of their order changes.

The problem

Sending SMSes costs a few cents each, and SMS Gateway does not provide a stable test environment. The development team has built a broad set of acceptance and performance tests, and perform these tests on a daily basis. Unfortunately, the cost of sending SMSes became significant.

The solution

To limit unnecessary costs, the development team decided to simulate the SMS Gateway using SmartMock.io.

The SMS Gateway's API is straightforward. It just accepts apiKey, message, sender, and number parameters as query parameters.

Sample request:

POST https://supersmsgateway.com/send?apiKey=82aaa1fd-dbec-43d6-8d9a-a6fe5bfd47f1&message=Very%20important%20message.&sender=John%20Doe&number=12025550142

Sample response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 150
X-RequestId: 8e9f0301-a316-4140-9cb7-834cd182470f

{
   "batchId":123456789,
   "cost":2,
   "numMessages":1,
   "message":{
      "numParts":1,
      "sender":"John Doe",
      "content":"Very important message"
   },
   "messages":[{
      "id":"0de209f4-5e39-477f-a727-c93641bfe2e8",
      "recipient": "12025550142"
   },
   "result":"success"
}

From the client’s perspective, the important parts of the response are the HTTP status code (which should be 200), result field (which should be success), and pair of X-RequestId header and batchId field, which are logged for auditing purposes. Both X-RequestId and batchId are expected to be unique for each request. To achieve uniqueness, the development team used a template mock with some random helpers.

Example 2 - Mocked SMS Sending Service

Mock of the success response

A mock with a success response should match the following fields:

  • HTTP Method: POST
  • Path: /send

The mock should return an HTTP status code of 200 OK and content-type of application/json. There should also be a random value generated for header X-RequestId, and a valid body returned.

Headers:
X-RequestId: {{randomUuid}}

Body:
{
   "batchId":{{now format='YYYYMMDDHH'}},
   "cost":2,
   "numMessages":1,
   "message":{
      "numParts":1,
      "sender":"{{req.query.[sender]}}",
      "content":"{{req.query.[message]}}"
   },
   "messages":[{
      "id":"{{randomUuid}}",
      "recipient": "{{req.query.[number]}}"
   },
   "status":"success"
}
  • Random UUID is set as X-RequestId header
  • batchId is an incremental value that changes every hour. It's simply the concatenation of the current year, month, day, and hour. For example 2018050610
  • Fields sender, content, and recipient, although the client does not use them, are set to values from the request
  • Field status is statically set to value success which is enough for the tested use case

Example 3. Payment Service Provider Integration (Dynamic Mock)

This example presents how dynamic mock and stateful behavior may be used to simulate delay of payment processing by a Payment Service Provider (PSP).

The problem

Some PSPs may return three statuses of the payment: Ready, Pending, and Failed. Depending on the response provided, the user interface should display a message about the payment being accepted or rejected, or display a spinner indicating the payment is being processed, as well as periodically poll Backend for Front-End (BFF) for the latest payment status. The problem is that the frequency of a Pending status being returned by a real PSP in any environment is very low, and its occurrences are non-deterministic. Because of this, it was impossible to test or to demo polling behavior.

Example 3 - Real Payment Service Provider

The solution

The development team decided to mock the PSP and use dynamic mocks to return a Pending status when the payment amount is 1500 else return a Ready status.

Example 3 - Mock Payment Service Provider

BFF uses two PSP methods: Create Payment and Get Payment Details.

Create Payment request sample:

POST /psp/creditcard/payments HTTP/1.1
Content-Type: application/json    
{
  "description" : "Some product description",
  "amount": 1500,
  "currency" : "USD",
  ... other payment data
}

Create Payment response sample:

HTTP/1.1 200 OK
Content-Type: application/json
{
  "payment": {
    "id": "5adc265f-f87f-4313-577e-08d3dca1a26c",
    "status": "Pending",
    ... other payment data
  }
}

Get Payment Details request sample:

GET /psp/creditcard/payments/5adc265f-f87f-4313-577e-08d3dca1a26c HTTP/1.1
Content-Type: application/json

Get Payment Details response sample:

HTTP/1.1 200 OK
Content-Type: application/json
{
  "payment": {
    "id": "5adc265f-f87f-4313-577e-08d3dca1a26c",
    "status": "Ready",
    ... other payment data
  }
}

Mock of Create Payment

Mock of the Create Payment endpoint should match the following request fields:

  • HTTP Method: POST
  • Path: /psp/creditcard/payments
  • Content Type: application/json

A dynamic mock is a suitable choice for the given use case. It should return a randomly generated payment id, and a status of Ready or Pending depending on the request amount value. Also, it must make use of the state feature to store payment details for further query. Duration of the Pending status is random (10 - 100 seconds):

// Set response status of 200 OK
res.status=200
 //Set content type of the response
res.addHeader('Content-Type', 'application/json'); 

// Generate payment ID
const id = faker.random.uuid();

// Choose payment status depending on payment amount
let status = 'Ready';
const amount = req.jsonBody.amount; //retrieve amount value from the request
if (amount === 1500) {
    status = 'Pending';
}

const payment = {id, status};

// Set response body
res.body= {
    payment: payment,
    // HATEOAS links
    operations: {
        method: "GET",
        href: `https://${req.host}/psp/creditcard/payments/${id}`,
        rel: "view-verification",
        contentType: "application/javascript"
    }
}

// Get state
const payments = state.getOrSet('payments', {})

// Put information about the reposne to state
payments[id] = {
    // Payment will be in Pending status up to 100 seconds from its creation
    pendingUntil: status === 'Pending' ? new Date().getTime() + faker.random.number({min:10000, max:100000}) : undefined,
    payment: payment
}

Mock of Get Payment Details

A mock of the Get Payment Details endpoint should match the following fields:

  • HTTP Method: GET
  • Path: /psp/creditcard/payments/{paymentId}
  • Content Type: application/json

A dynamic mock is also an appropriate choice for this mock. It needs to retrieve payment details from the state and set a Ready status if enough time has passed since payment creation.

//Set content type of the response
res.addHeader('Content-Type', 'application/json');

// Get state
const payments = state.getOrSet('payments', {});

// Retrieve response info from state
const paymentHolder = payments[req.pathParams['paymentId']];
if (paymentHolder) {
    //If pendingUntil defined and current time is larger than pendingUntil set payment status to 'Ready'
    if (paymentHolder.pendingUntil && new Date().getTime() > paymentHolder.pendingUntil) {
        paymentHolder.payment.status = 'Ready';
    }

    // Set resposne status code 200 OK and body 
    res.status=200;
    res.body={
        payment: paymentHolder.payment
    };
} else {
    // If payment not found return HTTO 404 Not Found with error details
    res.status=404;
    res.body={
        errorCode: 'PAYMENT_NOT_FOUND',
        errorMessage: 'Payment not found'
    }
}

Example 4. Simulate underperforming service (Proxy Mock)

The following example presents how to use proxy mock to simulate underperforming service having that service already implemented and available.

The problem

A company selling some physical goods has split the order handling process into multiple services. Two of these services are Order Service and Delivery Service. Order Service is responsible for handling the procedure of ordering goods, whereas the Delivery Service manages and keeps track of the delivery process. The user journey allows delayed deliveries, i.e., customers may pay for goods and request delivery at any random time after the payment has been completed. When the customer requests a delivery, the order should change its status to IN_DELIVERY, and there should be exactly one shipment record created in the Delivery Service. In case of connectivity problems, the Order Service is supposed to return PENDING_DELIVERY status and later synchronize with the Delivery Service automatically.

QA engineers would like to test some unhappy cases of the delayed delivery scenario, including a timeout between the Order Service and the Delivery Service. The following diagram presents the test scenario:

Example 4 - Delayed Delivery Test Scenario

Unfortunately, the real Delivery Service's response time is generally low, so it's impossible to execute deterministic tests.

Example 4 - Real Order Service to Delivery Service communication

One of the approaches to enable the QA team to implement deterministic tests is to implement delay logic in the Delivery Service itself. The significant disadvantage of that way is that test and production code is mixed. The more of such cases covered in the production code, the largest its pollution. Hosted HTTP mock solution (like SmartMock.io) helps to separate test code from production one.

The solution

SmartMock.io is a hosted HTTP mock solution that provides a feature of proxy mocks. SmartMock.io's HTTP proxy mocks with a configured response delays may be especially useful in addressing situations when a delay needs to be included in service-to-service calls. Order Service sends HTTP requests to proxy mocks, which forward them to the real Delivery Service. After the Delivery Service replies, SmartMock.io applies a constant delay before sending back a response to Order Service. Besides the mock introducing a delay, there's another mock to be created. It only forwards requests to the target service, so the response time is comparable with the direct Delivery Service call. The content of the Order Service request determines which mock matches: for the ones containing "zipCode": "00001" in the payload - delaying mock, all other requests - non-delaying mock.

Example 4 - Proxy Order Service requetss to Delivery Service

Mock of delayed Create Delivery

The delaying mock that forwards requests to the real Delivery Service should have the priority 0. Its responsibility is to simulate underperforming/unreachable Delivery Service.

It should match the following fields:

  • HTTP Method: POST
  • Path: /api/v1/delivery
  • Body contains: "zipCode": "00001"

It should also apply a constant response delay of 10 seconds, which is larger than the request timeout between the Order Service and the Delivery Service:

  • Delay type: Constant
  • Delay value (ms): 10000

The mock should be of the Proxy Mock type and should point to the real Delivery Service:

  • Response type: Proxy Mock
  • Target URL: the address of the real Delivery Service

Example 4 - delying mock form

Mock of non-delayed Create Delivery

The non-delaying mock that forwards requests to the real Delivery Service should have the priority 1. Its responsibility is to forward all other Create Delivery requests to the Delivery Service without a delay so other tests may check other scenarios.

It should match the following fields:

  • HTTP Method: POST
  • Path: /api/v1/delivery

The mock should be of the Proxy Mock type and should point to the real Delivery Service:

  • Response type: Proxy Mock
  • Target URL: the address of the real Delivery Service

Example 4 - non-delying mock form

Mock of Synchronize Delivery

Used by the Synchronization Job (see the diagram above). It does not delay the response but just forwards request to the Delivery Service.

  • HTTP Method: PUT
  • Path: /api/v1/delivery

The mock should be of the Proxy Mock type and should point to the real Delivery Service:

  • Response type: Proxy Mock
  • Target URL: the address of the real Delivery Service

Example 4 - synchronize mock form


Last update: September 2, 2020