DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Ways To Reduce JVM Docker Image Size
  • Aggregating REST APIs Calls Using Apache Camel
  • Build a REST API With Just 2 Classes in Java and Quarkus
  • High-Performance Reactive REST API and Reactive DB Connection Using Java Spring Boot WebFlux R2DBC Example

Trending

  • AI, ML, and Data Science: Shaping the Future of Automation
  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era
  • Building Scalable and Resilient Data Pipelines With Apache Airflow
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. How To Create a Stub in 5 Minutes

How To Create a Stub in 5 Minutes

Readers will learn how to create stubs in five minutes, which uses regression and load testing, debugging, and more, and how to configure the stubs flexibly.

By 
Andrei Rogalenko user avatar
Andrei Rogalenko
·
Jan. 17, 23 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
12.3K Views

Join the DZone community and get the full member experience.

Join For Free

If you’re developing an application that sends REST requests to the API of another service, mocks and stubs will probably help with your testing. Mock objects are used in unit tests: a call to another service is simulated. A test stub is an HTTP server that completely mimics the behavior of the service with which we plan to interact during the production use of our application. But using this service is not suitable for testing purposes. The scope of stubs is wide: regression testing, load testing, debugging, and more. 

Today, I will be as brief and, to the best of my ability, clearly talk about how to create stubs in a matter of minutes using the WireMock tool.

Options for Running the Stub

  • As a standalone process
  • In Docker
  • Plain Java server

You need to choose one of the options according to your needs.

Running as a Standalone Process 

I recommend using this particular stub launch option because it is simple and suitable in most situations. The undoubted advantage of this method is that there is no need to write code—the stub is completely configured using a JSON file. Although, if desired, you can also configure using the Java API.

To run the stub, we need the following:

  1. Download a ready-made JAR from the official site.
  2. Start a service that will listen on port 8080 by default (JRE required):
Shell
 
$ java -jar wiremock-jre8-standalone-2.33.2.jar


Some useful command line options:

  • --port: the HTTP port number.
  • --https-port: the HTTPS port number.
  • --verbose: turn on verbose logging to stdout. 
  • --root-dir: sets the root directory, under which mappings and files reside. This defaults to the current directory.
  • --container-threads: the number of threads created for incoming requests, which defaults to 10.
  • --local-response-templating: enable rendering of response definitions using Handlebars templates for specific stub mappings.

So the start of the service might look like this:

Shell
 
$ java -jar wiremock-jre8-standalone-2.33.2.jar –port 8090 --local-response-templating


Running in Docker

This option is not fundamentally different from the previous one. The stub is also configured using a JSON file.

  1. Download Docker image from the official site.
  2. Start a single WireMock container:
Shell
 
docker run -it --rm \
  -p 8080:8080 \
  --name wiremock \
  wiremock/wiremock:2.35.0


Plain Java Server 

In this option, we create our own application and configure the stub in it.

The simplest example:

Java
 
public static void main(String[] args) {

    WireMockServer server = new WireMockServer(
            options()
                    .port(8080)                    
    );

    server.start();
    /*
	Configure the stub here
    */
}


To build the application, we need to specify the dependencies.

Maven (Java 8)

XML
 
<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <version>2.33.2</version>    
</dependency>


Gradle (Java 8)

Groovy
 
Implementation "com.github.tomakehurst:wiremock-jre8:2.33.2"


Configuring and Running Stubs

The Simplest Stub

It’s time to write a simple stub and test it out.

First, let’s configure the stub via a JSON file:

JSON
 
{
    "request": {
        "method": "GET",
        "url": "/some/thing"
    },
    "response": {
        "status": 200,
        "body": "Hello world!",
        "headers": {
            "Content-Type": "text/plain"
        }
    }
} 


We place the file in the /mappings directory, where we consider the root directory to be the directory where your JAR file is located (standalone), or in the /mappings directory in the Docker container.

Let’s check the functionality. We run the stub on the local machine and listen on port 8080. Send a GET request to the following address: http://localhost:8080/some/thing.

Shell
 
$ curl http://localhost:8080/some/thing

Hello world!


As you can see, the request returns status 200 and the body «Hello world!»

If you are creating your own Java Server with a stub, then the code will be as follows: 

Java
 
public static void main(String[] args) {

    WireMockServer server = new WireMockServer(
            options()
                    .port(8080)
                    .containerThreads(20)
                    .jettyAcceptors(10)
                    .extensions(new ResponseTemplateTransformer(false))
    );

    server.start();
    
    stubFor(get(urlEqualTo("/some/thing"))
            .willReturn(aResponse()
                    .withHeader("Content-Type", "text/plain")
                    .withBody("Hello world!")));

}


Request Matching

Requests to the stub can be varied and differ from each other not only by the endpoint, but by the following attributes:

  • HTTP method
  • Query parameters
  • Headers
  • Cookies
  • Request body

And for each case, we can write our own stub. That is, for example, for two identical requests that differ from each other only in headers, you can write two separate stubs, laying down your own logic depending on the headers used.

Here are some examples of the most used cases.

URL Matching

URLs can be matched either by equality or by regular expression.

JSON
 
{
  "request": {
    "urlPathPattern": "/endpoint[123]"
    ...
  },
  ...
}


Java
 
urlPathMatching("/endpoint[123]")


Headers Matching

Checking for a header «Content-Type: application/json».

JSON
 
{
  "request": {
    ...
    "headers": {
      "Content-Type": {
        "equalTo": "application/json"
      }
    }
    ...
  },
  ...
}


Java
 
.withHeader("Content-Type", equalTo("application/json"))


Query Parameters

Let’s check that the request has an ID parameter and the value contains only numbers (we use a regular expression).

JSON
 
{
  "request": {
    ...
    "queryParameters" : {
      "id" : {
        "matches" : "\d"
      }
    }
    ...
  },
  ...
}


Java
 
.withQueryParam("id", matching("\d"))


Request Body

Check to see if the body matches the given JSON:

JSON
 
{
  "request": {
    ...
    "bodyPatterns" : [ {
      "equalToJson" : { "total_results": 4 }
    } ]
    ...
  },
  ...
}


Java
 
.withRequestBody(equalToJson("{ \"total_results\": 4 }"))


The same, but XML:

JSON
 
{
  "request": {
    ...
    "bodyPatterns" : [ {
      "equalToXml" : "<thing>Hello</thing>"
    } ]
    ...
  },
  ...
}


Java
 
.withRequestBody(equalToXml("<thing>Hello</thing>"))


Response Templating

When forming headers or the response body, it is possible to use data from the incoming request using the Handlebars template mechanism.

To enable this mechanism, if you configure the stub via JSON, you need to add the key transformers (array) with the value “response-template.”

JSON
 
{
    "request": {
        "urlPath": "/templated"
    },
    "response": {
        "body": "{{request.path.[0]}}",
        "transformers": ["response-template"]
    }
}


If you are running the stub as a standalone process, you will need the --local-response-templating command line switch to enable the templating engine.

For the stub as a Plain Java Server, an example of enabling the Handlebars templating mechanism has already been given above (see “The Simplest Stub” header).

Java
 
WireMockServer server = new WireMockServer(
            options()
                    .port(8080)
                    .containerThreads(20)
                    .jettyAcceptors(10)
                    .extensions(new ResponseTemplateTransformer(false))
    );


The model of the request is supplied to the header and body templates. The following request attributes are available: 

  • request.url: URL path and query
  • request.path: URL path
  • request.query.<key> : first value of a query parameter, e.g., request.query.search.
  • request.method: request method, e.g., POST
  • request.host: hostname part of the URL, e.g., my.example.com.
  • request.port: port number e.g. 8080
  • request.scheme: protocol part of the URL, e.g., https.
  • request.baseUrl: URL up to the start of the path, e.g., https://my.example.com:8080.
  • request.headers.<key>: first value of a request header, e.g., request.headers.X-Request-Id.
  • request.cookies.<key>: first value of a request cookie, e.g., request.cookies.JSESSIONID.
  • request.body: request body text

Incoming request parameters or headers can be either single values or arrays. Therefore, templates provide convenient ways to work with data. For example, the incoming request URL /multi-query?things=1&things=2&things=3.

 
{{request.query.things}} // Will return 1
{{request.query.things.0}} // Will return 1
{{request.query.things.first}} // Will return 1
{{request.query.things.1}} // Will return 2
{{request.query.things.[-1]}} // Will return 2
{{request.query.things.last}} // Will return 3


There are also a fairly large number of functions used in Handlebars templates for data manipulation. We use some of them in the example of a complex stub, which I will demonstrate below.

Complex Stub

It’s time to show a more complex example that demonstrates the power of stubs.

Let’s configure the stub via a JSON file:

JSON
 
{
	"request": {
		"method": "POST",
		"urlPathPattern": "/templated[1234]",
		"headers": {
			"Content-Type": {
				"equalTo": "application/json"
			}
		}
	},
	"response": {
		"status": 200,
		"headers": {
			"Content-Type": "application/json"
		},
		"bodyFileName": "respBody.json",
		"transformers": [
			"response-template"
		]
	}
}


We see that we used a regular expression in the request URL. That is, we can access our stub using any of the four endpoints: 

  1. /templated1 
  2. /templated2 
  3. /templated3 
  4. /templated4

We also see from the configuration that only POST requests with the "Content-Type": "application/json" header will be processed. The exact same header will be added to the response. When generating a response, the Handlebars templating mechanism will be enabled. The response will be generated based on the respBody.json file.

In the /__files directory of the working directory of our stub, create a new respBody.json file with the following content:

JSON
 
{
	"resp": {
		"RqUID": "{{jsonPath  request.body '$.req.sys.RqUID'}}",
		"RqTm": "{{jsonPath  request.body '$.req.sys.RqTm'}}",
		"CardNum": "{{jsonPath  request.body '$.req.data.CardNum'}}",
		"date": "{{now}}",
		"randUID": "{{randomValue length=12 type='ALPHANUMERIC' uppercase=true}}",
		"someValue": "value12345"
	}
}


The file is basically standard JSON. Fields that will be transformed using the Handlebars templating mechanism are enclosed in double curly braces {{}}.

Here are some features of the presented transformations:

  • Using the jsonPath function, the values of the req.sys.RqUID, req.sys.RqTm, and req.data.CardNum tags are extracted from the JSON structure (the body of the incoming request).
  • Using the now function, we got the current system date and time.
  • Using the randomValue function, we got a random value consisting of letters and numbers of a given length.

 Let’s check how it works. We send a POST request to the following address: http://localhost:8080/templated2.

Request Body:

JSON
 
{
	"req": {
		"sys": {
			"RqUID": "1234567890f",
			"RqTm": "2022-09-22T12:06:54"
		},
		"data": {
			"CardNum": 3492374932472394
		}
	}
}


We will get the following answer:

JSON
 
{
	"resp": {
		"RqUID": "1234567890f",
		"RqTm": "2022-09-22T12:06:54",
		"CardNum": "3492374932472394",
		"date": "2022-08-31T13:14:27Z",
		"randUID": "PLOVSWNXQJR9",
		"someValue": "value12345"
	}
}


Conclusion

We are now able to create stubs in a matter of minutes and configure them very flexibly. Good luck!

JSON Docker (software) JAR (file format) Java (programming language) Stub (distributed computing) API JRE REST POST (HTTP)

Opinions expressed by DZone contributors are their own.

Related

  • Ways To Reduce JVM Docker Image Size
  • Aggregating REST APIs Calls Using Apache Camel
  • Build a REST API With Just 2 Classes in Java and Quarkus
  • High-Performance Reactive REST API and Reactive DB Connection Using Java Spring Boot WebFlux R2DBC Example

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!