How to Create a Standalone Wiremock Stub Server

In this tutorial, I will explain how to create a standalone WireMock stub server for stubbing APIs. We will use, WireMock and JAVA for this stub setup. Creating a standalone stub server is one of the important parts of API test automation. We need to create stubs or mocks when the API under test is not completely ready or we need to test the external systems without getting affected by any external environmental problems. We are also getting much faster responses from the stubs/mocks rather than real implementations that run in an environment.

First, I will define our API requirements. We will define a shopping cart API swtestacademyshop.com and then start to create a stub for this API. In another article, we will create an API Test Automation Framework with Rest Assured to test this API by running our Standalone stub server. We will create a stub project separately in this article but if you want, you can also combine them in one project.

Note: swtestacademyshop.com is an artificial website, it is not a real website.

This article’s focus is the create a standalone stub server with Wiremock for swtestacademyshop’s shopping cart API. Let’s start with defining our sample service which is a shopping cart service for swtestacademyshop.com. After shopping cart API design, we will start to create our WireMock stub server project step by step.

Design a Sample Service and Define Requirements 

Now, let’s wear a solution designer/analyst hat and define our API requirements and design. Our service has all the below specifications.

  • Endpoint URL (Base path and resource path)
  • Body
  • Query Parameters
  • Path Parameters
  • Headers

Generally, services have the above components. Some of them have them all, some of them haven’t. Our service has all of them. On swtestacademyshop, we will sell courses. Our product type is courses and we can do below operations for shoppingcart service:

  • Create a shopping cart. (POST)
  • Get/Retrieve shopping cart details. (GET)
  • Add a course to the shopping cart. (POST)
  • Update a course in the shopping cart. (PUT)
  • Delete a course from the shopping cart. (DELETE)
  • Delete all shopping carts. (DELETE)

Now let’s define the service requirements step by step.

Service URL

Base URL http://test.swtestacademyshop.com
Base Path carts

Create a Shopping Cart

Method: POST

Resource Path: No resource path required.

Example Request: POST /carts

Body:

I defined a very basic simple shopping cart body as shown below.

This article is not a comprehensive API design article thus I don’t want to go into too much detail and I don’t want to write all min, max, mandatoriness requirements of each field.

Our shopping cart has customer and products attributes and they have their own attributes as shown below.

{
   "customer":{
      "id":"872738729",
      "firstName":"Onur",
      "lastName":"Baskirt",
      "email":"[email protected]"
   },
   "products":[
      {
         "description":"Rest Api Testing with Rest Assured and Spring.",
         "type":"Course",
         "productAttributes":[ 
            { 
               "courseLenght":"5 hours",
               "courseCreationDate":"2021-01-14",
               "courseLevel":"Advanced"
            }
         ],     
         "price":{
            "amount": 80.0,
            "discount": 30.0,
            "paymetType":"CREDIT CARD",
            "currency": "USD"
         }
      }
   ]
}

Also, you can use http://jsonviewer.stack.hu/ to see JSON documents nicer.

create shopping cart json

Headers:

Content-Type application/json
Accept application/json
Version v1
Client Android
Authorization Bearer <token>

We do not have any path or query parameters for creating a cart operation.

Response:

Below is our sample create cart response. It contains only one course and as you see below, we have shoppingCartId and productId attributes.

HTTP Status Code: 201 CREATED

{
  "id": "klms2f4c-8129-4a4b-b32d-550b7fc3cfb2",
  "customer": {
    "id": "872738729",
    "firstName": "Onur",
    "lastName": "Baskirt",
    "email": "[email protected]"
  },
  "products": [
    {
      "id": "93b55282-334c-48b0-a8f7-a9d5eef9c4b9",
      "description": "Rest Api Testing with Rest Assured and Spring.",
      "type": "Course",
      "productAttributes": [
        {
          "courseLenght": "5 hours",
          "courseCreationDate": "2021-01-14",
          "courseLevel": "advanced"
        }
      ],
      "price": {
        "amount": 80.0,
        "discount": 30.0,
        "paymetType": "CREDIT CARD",
        "currency": "USD"
      }
    }
  ]
}

Get a Shopping Cart

Method: GET

Resource Path: {cartId}

Query Param: productCount (I added to distinguish 1 and 2 product mocking. Normally, we do not need this kind of QueryParams.)

Example Request: GET /carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2?productCount=1

Response:

HTTP Status Code: 200 OK

{
  "id": "klms2f4c-8129-4a4b-b32d-550b7fc3cfb2",
  "customer": {
    "id": "872738729",
    "firstName": "Onur",
    "lastName": "Baskirt",
    "email": "[email protected]"
  },
  "products": [
    {
      "id": "93b55282-334c-48b0-a8f7-a9d5eef9c4b9",
      "description": "Rest Api Testing with Rest Assured and Spring.",
      "type": "Course",
      "productAttributes": [
        {
          "courseLenght": "5 hours",
          "courseCreationDate": "2021-01-14",
          "courseLevel": "advanced"
        }
      ],
      "price": {
        "amount": 80.0,
        "discount": 30.0,
        "paymetType": "CREDIT CARD",
        "currency": "USD"
      }
    }
  ]
}

Add a Product to Shopping Cart

Method: POST

Resource Path: {cartId}/products

Body:

{
  "products": [
    {
      "description": "Selenium Webdriver Tutorial.",
      "type": "Course",
      "productAttributes": [
        {
          "courseLenght": "4 hours",
          "courseCreationDate": "2020-08-15",
          "courseLevel": "intermediate"
        }
      ],
      "price": {
        "amount": 60.0,
        "discount": 20.0,
        "paymetType": "CREDIT CARD",
        "currency": "USD"
      }
    }
  ]
}

Example Request: POST /carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2/products

Response:

HTTP Status Code: 201 CREATED

{
  "products": [
    {
      "id": "94b55282-334c-48b0-a8f7-a9d5eef9c4c9",
      "description": "Selenium Webdriver Tutorial.",
      "type": "Course",
      "productAttributes": [
        {
          "courseLenght": "4 hours",
          "courseCreationDate": "2020-08-15",
          "courseLevel": "intermediate"
        }
      ],
      "price": {
        "amount": 60.0,
        "discount": 20.0,
        "paymetType": "CREDIT CARD",
        "currency": "USD"
      }
    }
  ]
}

Update a Product to Shopping Cart

Method: PUT

Resource Path: {cartId}/products/{productId}

Example Request: PUT /carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2/products/93b55282-334c-48b0-a8f7-a9d5eef9c4b9

Body:

{
  "description": "Rest Api Testing with Rest Assured and Spring.",
  "type": "Course",
  "productAttributes": [
    {
      "courseLenght": "5 hours",
      "courseCreationDate": "2021-01-14",
      "courseLevel": "advanced"
    }
  ],
  "price": {
    "amount": 91.42,
    "discount": 20.0,
    "paymetType": "CREDIT CARD",
    "currency": "USD"
  }
}

Response:

HTTP Status Code: 200 OK

{
  "id": "93b55282-334c-48b0-a8f7-a9d5eef9c4b9",
  "description": "Rest Api Testing with Rest Assured and Spring.",
  "type": "Course",
  "productAttributes": [
    {
      "courseLenght": "5 hours",
      "courseCreationDate": "2021-01-14",
      "courseLevel": "advanced"
    }
  ],
  "price": {
    "amount": 91.42,
    "discount": 20.0,
    "paymetType": "CREDIT CARD",
    "currency": "USD"
  }
}

Delete a Product from Shopping Cart

Method: DELETE

Resource Path: {cartId}/products/{productId}

Example Request: DELETE /carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2/products/93b55282-334c-48b0-a8f7-a9d5eef9c4b9

Body: No Body!

Response:

HTTP Status Code: 204 NO CONTENT

Delete Shopping Cart

Method: DELETE

Resource Path: {cartId}

Example Request: DELETE /carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2

Body: No Body!

Response:

HTTP Status Code: 204 NO CONTENT

Standalone WireMock Stub Server Creation

Now, we can create a stub server based on our requirements. These requirements are just a sample and not production level requirements. We are just trying to learn how to use wire mock that’s why please do not stick the design and requirements part so much.

Based on the requirements of the swtestacademy shopping cart service, we can start our project.

Step-1: Add Required Dependencies to pom.xml

Please use recent libraries. When I am writing this article, I used the recent ones in this pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>swtestacademyshopstub</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock</artifactId>
            <version>2.25.1</version>
        </dependency>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20190722</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.5.2</version>
        </dependency>
    </dependencies>
</project>

Step-2: Create Mock JSON Files

For each operation CREATE, ADD, UPDATE, etc. we should create sample request and response files based on the requirements and we need to locate them under:

test -> resources -> __files package. I also added an extra one more package as JSON but it is not necessary because we are creating a stub server for REST API with JSON. We do not have any XML communication.

Standalone Wiremock Server

I don’t want to copy and paste all JSON files here. You can check them in GitHub link.

Step-3: Coding the Stub Server

The structure of the code is not complex. I want to create a stub server as simply as possible. We have one util class for JSON operations, one main class, and one stub class for building/creating the stubs. Also, you can add some controller classes to make this stub server more intelligent. In this article, I do not want to share too much complexity.

stubs

Also, you have to know how to deal with pattern matching for both URLs and Bodies. For this, please check official Wiremock documentation. They shared many examples. Here is the link: http://wiremock.org/docs/request-matching/

Let’s start with JsonUtil class.

JsonUtil.java

This class does a very basic JSON read operation. We will use setJSON and getJSON methods in our stub creation methods.

package utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.json.JSONObject;
import org.json.JSONTokener;

public class JsonUtil{
    public JSONObject jsonObject;
    private File jsonFile;

    public JSONObject getJSON() {
        return jsonObject;
    }

    public JsonUtil setJSON(String path) {
        jsonFile = new File(System.getProperty("user.dir") + "/src/test/resources/" + path);
        jsonObject = readJSONAsJSONObject();
        return this;
    }

    public JSONObject readJSONAsJSONObject() {
        InputStream is = null;
        try {
            is = new FileInputStream(jsonFile);
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        JSONTokener jsonTokener = new JSONTokener(is);
        jsonObject = new JSONObject(jsonTokener);
        return jsonObject;
    }
}

Stubs.java

In this class, we will create our mocks. Based on Wiremock functionalities, we will use different approaches to create these mocks. Please, try to understand the below code by yourself and also check wiremock documentation. If you have questions, pleae post a comment. I will do my best to reply to your questions.

import com.github.tomakehurst.wiremock.WireMockServer;
import utils.JsonUtil;

import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class Stubs {
    private JsonUtil jsonUtil;
    public WireMockServer wireMockServer;

    public Stubs setUp() {
        wireMockServer = new WireMockServer(3467);
        wireMockServer.start();
        jsonUtil = new JsonUtil();
        return this;
    }

    public Stubs resetServer() {
        wireMockServer.resetAll();
        return this;
    }

    public Stubs stubForCreateCart(String responseFileName) {
        wireMockServer.stubFor(post("/carts")
            .withHeader("Content-Type", equalToIgnoreCase("application/json"))
            .withHeader("Accept", equalToIgnoreCase("application/json"))
            .withHeader("Version", equalToIgnoreCase("v1"))
            .withHeader("Client", equalToIgnoreCase("Android"))
            .withHeader("Authorization", equalToIgnoreCase("Bearer SWTestAcademyShopSecretToken"))
            .withRequestBody(matchingJsonPath("$.customer.firstName", equalTo("Onur")))
            .withRequestBody(matchingJsonPath("$.customer.lastName", equalTo("Baskirt")))
            .willReturn(aResponse()
                .withStatus(201)
                .withHeader("Content-Type", "application/json")
                .withBodyFile("json/" + responseFileName)));
        return this;
    }

    public Stubs stubForCreateCartError(String responseFileName) {
        wireMockServer.stubFor(post("/carts")
            .withHeader("Authorization", equalToIgnoreCase("Bearer Error"))
            .willReturn(aResponse()
                .withStatus(401)
                .withHeader("Content-Type", "application/json")
                .withBodyFile("json/" + responseFileName)));
        return this;
    }

    public Stubs stubForGetCartSingle(String responseFileName) {
        wireMockServer.stubFor(get("/carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2?productCount=1")
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBodyFile("json/" + responseFileName)));
        return this;
    }

    public Stubs stubForGetCartDouble(String responseFileName) {
        wireMockServer.stubFor(get("/carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2?productCount=2")
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBodyFile("json/" + responseFileName)));
        return this;
    }

    public Stubs stubForAddProduct(String requestFileName, String responseFileName) {
        jsonUtil.setJSON("/__files/json/".concat(requestFileName));
        wireMockServer.stubFor(post("/carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2")
            .withRequestBody(equalToJson(jsonUtil.getJSON().toString(), true, true))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBodyFile("json/" + responseFileName)));
        return this;
    }

    public Stubs stubForPutProduct(String requestFileName, String responseFileName) {
        jsonUtil.setJSON("/__files/json/".concat(requestFileName));
        wireMockServer.stubFor(post("/carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2/products/93b55282-334c-48b0-a8f7-a9d5eef9c4b9")
            .withRequestBody(equalToJson(jsonUtil.getJSON().toString(), true, true))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBodyFile("json/" + responseFileName)));
        return this;
    }

    public Stubs stubForDeleteProduct() {
        wireMockServer.stubFor(delete("/carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2/products/93b55282-334c-48b0-a8f7-a9d5eef9c4b9")
            .willReturn(aResponse()
                .withStatus(204)));
        return this;
    }

    public Stubs stubForDeleteCart() {
        wireMockServer.stubFor(delete("/carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2")
            .willReturn(aResponse()
                .withStatus(204)));
        return this;
    }

    public Stubs status() {
        System.out.println("Stubs Started!");
        return this;
    }
}

StubServerMain.java

We will chain our stub creation methods in the main class. :) That’s all we do in this class.

public class StubServerMain {

    public static Stubs stubs = new Stubs();

    public static void main(String[] args) {
        stubs.setUp()
            .stubForCreateCart("CreateCartSuccessResponse.json")
            .stubForCreateCartError("CreateCartErrorResponse.json")
            .stubForGetCartSingle("CreateCartSuccessResponse.json")
            .stubForGetCartDouble("GetCartDoubleResponse.json")
            .stubForAddProduct("AddProductRequest.json","AddProductResponse.json")
            .stubForPutProduct("PutProductRequest.json","PutProductResponse.json")
            .stubForDeleteProduct()
            .stubForDeleteCart()
            .status();
    }
}

After all of these steps, you should run and test your stub server.

First, create an application run in the run panel of IntelliJ.

intelliJ Settings

Then run it.

run stub server

And then open the PostMan and test the stub server as shown below.

test stubs with postman

GitHub URL: https://github.com/swtestacademy/wiremock-stub-server

Postman Collection: https://www.getpostman.com/collections/f1e6e53de1f2ab77a6d1

Thanks.
Onur

7 thoughts on “How to Create a Standalone Wiremock Stub Server”

  1. Hey Onur,

    Thanks for sharing this work made my day! I slightly changed the context and added a pattern that helps segregating multiple service class layers.

    github.com/ozinal/wiremock-stub-server

    Bayram

    Reply
  2. Hi Onur,

    I see stubs.java returning the response. Any way we can convert the response to Pojo ( MyResponse ) class ?

    Reply
    • Yes sure, you can do it by using “jackson-databind” library.

      Below I am sharing a sample code.

      Supplier OBJECT_MAPPER = () -> {
      final ObjectMapper mapper = new ObjectMapperConfigurer(new ObjectMapper()).getObjectMapper();
      mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
      mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
      return mapper;
      };

      /**
      * This method getObjectFromJson will return jsonAsObject.
      *
      * @param json input parameter
      * @param type input parameter
      * @return jsonAsObjectFromString
      */
      static < T > T getObjectFromJson(final String json, final Class< T > type) {
      try {
      return OBJECT_MAPPER
      .get()
      .readValue(json, type);
      } catch (final IOException e) {
      throw new IllegalStateException(e);
      }
      }

      Reply
  3. i need to use wiremockServer without wiremock jar. i need it in java code.
    can you please help here .

    please share the code snippet

    Reply
  4. Hi Ankur,
    Great article, thanks. A query below..
    In the stubForPutProduct, shouldn’t the stub creation be stubFor(put(.. instead of subtFor(post(..

    public Stubs stubForPutProduct(String requestFileName, String responseFileName) {
    jsonUtil.setJSON(“/__files/json/”.concat(requestFileName));
    wireMockServer.stubFor(post(“/carts/klms2f4c-8129-4a4b-b32d-550b7fc3cfb2/products/93b55282-334c-48b0-a8f7-a9d5eef9c4b9”)
    .withRequestBody(equalToJson(jsonUtil.getJSON().toString(), true, true))
    .willReturn(aResponse()
    .withStatus(200)
    .withHeader(“Content-Type”, “application/json”)
    .withBodyFile(“json/” + responseFileName)));
    return this;
    }
    ……

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.