Stubbing
- Simplify response related stubbing
- Incorrect configuration in response definitions
- Response definition body file paths
- Convert between various response body definition modes
- Duplicate and redundant configuration (Java DSL)
- Duplicate configuration (JSON DSL)
- Code completion for Content-Type header values
- Code completion of GRPC status name header values
- Validation of GRPC status name header values
Simplify response related stubbing
There are many convenience methods available in the WireMock
class to simplify and shorten stubbing of responses.
This inspection reports cases when response specific stubbing may be replaced with a shorter version, also providing a quick fix for each replacement. For example:
NOTE: Since the form WireMock.aResponse().withStatus(...)
(for status codes that do not have convenience methods) might be more descriptive than using WireMock.status(...)
, there is a flag in the inspection’s settings whether to report such cases. It is disabled by default.
Status 2xx
From: aResponse().withStatus(200)
to: ok()
From: aResponse().withStatus(200).withBody(body)
ok().withBody(body)
to: ok(body)
From: aResponse().withStatus(200).withHeader(CONTENT_TYPE, contentType).withBody(body)
ok().withBody(body).withHeader(CONTENT_TYPE, contentType)
ok(body).withHeader(CONTENT_TYPE, contentType)
to: okForContentType(contentType, body)
From: aResponse().withStatus(201)
to: created()
From: aResponse().withStatus(204)
to: noContent()
From: okForContentType("application/json", body)
ok().withBody(body).withHeader("Content-Type", "application/json")
aResponse().withStatus(200).withBody(body).withHeader("Content-Type", "application/json")
to: okJson(body)
From: okForContentType("application/xml", body)
ok().withBody(body).withHeader("Content-Type", "application/xml")
aResponse().withStatus(200).withBody(body).withHeader("Content-Type", "application/xml")
to: okXml(body)
From: okForContentType("text/xml", body)
ok().withBody(body).withHeader("Content-Type", "text/xml")
aResponse().withStatus(200).withBody(body).withHeader("Content-Type", "text/xml")
to: okTextXml(body)
Status 3xx
From: aResponse().withStatus(301).withHeader("Location", location)
to: permanentRedirect(location)
From: aResponse().withStatus(302).withHeader("Location", location)
to: temporaryRedirect(location)
From: aResponse().withStatus(303).withHeader("Location", location)
to: seeOther(location)
Status 4xx
From: aResponse().withStatus(400)
to: badRequest()
From: aResponse().withStatus(401)
to: unauthorized()
From: aResponse().withStatus(403)
to: forbidden()
From: aResponse().withStatus(404)
to: notFound()
From: aResponse().withStatus(422)
to: badRequestEntity()
Status 5xx
From: aResponse().withStatus(500)
to: serverError()
From: aResponse().withStatus(503)
to: serviceUnavailable()
Other
From: aResponse().withStatus(status)
to: status(status)
Incorrect configuration in response definitions
This inspection is an umbrella for reporting any issue that is deemed incorrect when configuring stub response definitions.
Currently, it reports ResponseDefinitionBuilder#withBodyFile()
calls whose path parameters start with the /
symbol.
According to the WireMock Stubbing documentation:
Body file paths should always be relative i.e. not have a leading /
Java
public class ATest {
private static final String INCORRECT_PATH = "/incorrect/path";
void pathValidation() {
stubFor(post("/").willReturn(aResponse().withBodyFile("/incorrect/path")));
stubFor(post("/").willReturn(aResponse().withBodyFile(INCORRECT_PATH)));
}
}
JSON
{
"request": { ... },
"response": {
"status": 200,
"bodyFileName": "/incorrect/path"
}
}
Response definition body file paths
Gutter icons
ResponseDefinitionBuilder#withBodyFile()
takes argument a path, relative to a __files
directory (by default src/test/resources/__files
,
see Specifying the response body), to a file providing the contents of the response body.
In JSON mappings the response.bodyFileName
property behaves the same.
To mark the location of such files, a line marker/gutter icon is added if the path can be resolved, to which the file reference is always added.
Java
The icon is displayed if the path can be resolved to any of the __files
directories in the project, if there are multiple.
JSON
The icon is displayed if the path can be resolved to the _files
directory sibling of the mapping file’s parent (at any level)mappings
directory .
References
Similarly to the gutter icons detailed in the previous section, references are also provided for the file paths (both in JSON and Java DSLs), so that users can navigate to them via Ctrl+Click and similar actions.
Such references also make it possible for the response body files to show up in usage based searches, for instance when removing the response body file, but its path is still referenced in mapping files, or test code.
Convert between various response body definition modes
WireMock provides multiple ways to specify response bodies, so naturally there can be various ways of conversion between them. The aim of the following intentions is to help with those conversions.
NOTE: currently, whether a JSON file is an actual mapping file, is determined mainly by whether it is located (directly or indirectly) in a folder called mappings.
Inline the contents of a body file as a body string
The intentions below are available when:
- Java DSL: the caret is at the
withBodyFile()
method call identifier, and the file path can be resolved to a valid file in any of the__files
folders within the project. - JSON DSL: at any part of the
response.bodyFileName
property, and the file path can be resolved to a valid file in the__files
folder under the same root where the current mapping file is located.
Upon conversion, the JSON string is not pretty-printed or minified, it will contain line breaks, indentation, etc., but it is escaped according to Java/JSON escaping rules.
Also, there is no check for the usage of the source file it was converted from whether it could be removed or not.
Java
It converts ResponseDefinitionBuilder#withBodyFile()
calls to withBody()
calls replacing the referenced file path with the contents of the file as the argument body.
If the file path is referenced via a constant, the constant declaration is kept even if it is not used anywhere else. It is up to the users to decide whether they want to keep it or delete it.
The intention can locate stub files in any of the __files
folders in the current project. In case multiple of them is found on the same relative path, users can choose which one they want to inline the contents of.
from: aResponse().withBodyFile("com/wiremocha/a_response_body.json");
to: aResponse().withBody("{\n \"name\": \"value\"\n}"); //given that a_response_body.json contained that text
to: aResponse().withBody("""
{
"name": "value"
}"""); //Inlines the contents into a text block when the configured JDK is version 17 or higher
JSON
This one converts response.bodyFileName
properties to body
ones replacing the referenced file path with the contents of the file as the property value.
//from:
"response": {
"bodyFileName": "com/wiremocha/a_response_body.json"
}
//to:
"response": {
"body": "{\n \"name\": \"value\"\n}"
}
Extract string body into a body file
This intention makes it possible to extract a response body string into a separate file, and replace it with its relative path to the __files
directory.
Java
It replaces ResponseDefinitionBuilder#withBody(<response body string>)
calls with withBodyFile(<relative path to __files directory>)
ones along with creating the new response body file in the target directory.
It is available when the selected method call is ResponseDefinitionBuilder#withBody(String)
, and there is at least one __files
directory in the current project.
Workflow/extraction process
from: aResponse().withBody("{\n \"name\": \"value\"\n}");
to: aResponse().withBodyFile("com/wiremocha/a_response_body.json");
//The path "com/wiremocha/a_response_body.json" is relative to a __files directory wherever it is located in the project.
JSON
The logic is pretty much the same for the JSON DSL with the following differences:
- It is available when the selected property is
response.body
with a string literal value, in an actual mapping file, and there is a__files
directory under the same root as the mapping file. - The body file is created in the
__files
directory under the same root as the mapping file is located. (However, since a target sub-folder can be selected also, when there is any, there is no restriction to extract the body to any location.)
//from:
"response": {
"body": "\"{\n \"name\": \"value\"\n}\""
}
//to:
"response": {
"bodyFileName": "com/wiremocha/a_response_body.json"
}
Base64-encoded body string
These intentions help replace string literal response bodies with their base64-encoded values. The encoding logic is based on the GuavaBase64Encoder
class what WireMock uses for encoding. And, since padding can be kept or omitted, there are separate intentions for the two modes.
Java
It converts ResponseDefinitionBuilder#withBody()
calls to #withBase64Body()
calls replacing the body with its base64-encoded value, and is available when the caret is at the withBody()
method call identifier.
public static final String STRING_CONSTANT = "\"string\"}";
from: aResponse().withBody("{\"aJson\": \"string\"}");
from: aResponse().withBody("""
{"aJson": "string"}""");
from: aResponse().withBody("{\"aJson\": " + "\"string\"}");
from: aResponse().withBody("{\"aJson\": " + STRING_CONSTANT);
to (with padding): aResponse().withBase64Body("eyJuYW1lIjogInZhbHVlIn0=")
to (w/o padding): aResponse().withBase64Body("eyJuYW1lIjogInZhbHVlIn0")
JSON
It converts response.body
properties to response.base64Body
ones in JSON mapping files, replacing the body with its base64-encoded value. It is available when the caret is at the response.body
property, and the argument value is a string literal.
//from:
"response": {
"body": "{\"name\": \"value\"}"
}
//to (with padding):
"base64Body": "eyJuYW1lIjogInZhbHVlIn0="
//to (w/o padding):
"base64Body": "eyJuYW1lIjogInZhbHVlIn0"
Decode base64-encoded body
These intentions help replace base64-encoded response bodies with their decoded values. The decoding logic is based on the GuavaBase64Encoder
class what WireMock uses for decoding.
If, upon invoking the intention, the argument cannot be decoded due to being an invalid base64 string, an error hint is displayed with the reason.
Java
It converts ResponseDefinitionBuilder#withBase64Body()
calls to #withBody()
calls replacing the base64-encoded string with its decoded value, and is available when the caret is at the withBase64Body()
method call identifier.
from: aResponse().withBase64Body("eyJuYW1lIjogInZhbHVlIn0=");
to: aResponse().withBody("{\"name\": \"value\"}");
JSON
It converts response.base64Body
properties to response.body
ones in JSON mapping files, replacing the base64-encoded string with its decoded value. It is available when the caret is at the response.base64Body
property, and the argument value is a string literal.
//from:
"response": {
"base64Body": "eyJuYW1lIjogInZhbHVlIn0="
}
//to:
"response": {
"body": "{\"name\": \"value\"}"
}
Duplicate and redundant configuration (Java DSL)
This inspection reports duplicate and redundant configuration in ResponseDefinitionBuilder
, MappingBuilder
and RequestPatternBuilder
call chains.
withHeader()
: When a header name is specified in multiple.withHeader()
calls, that header’s value would be overridden.withQueryParam()
: When a query param name is specified in multiple.withQueryParam()
calls, that header’s value would be overridden.
Quick fixes are also available to remove duplicate calls for the selected query param or header name. It keeps the call that the user invokes the quick fix on.
ResponseDefinitionBuilder
withHeader
//Both "Accept-Language" string literals are highlighted
aResponse().withHeader("Accept-Language", "HU").withHeader("Content-Length", "1024").withHeader("Accept-Language", "JP");
//Both ACCEPT_LANGUAGE constant expressions are highlighted
aResponse().withHeader(ACCEPT_LANGUAGE, "HU").withHeader("Content-Length", "1024").withHeader(ACCEPT_LANGUAGE, "JP");
A quick fix is also available (since v1.0.1) to remove duplicate calls for the header name. It keeps the call that the user invokes the quick fix on:
//From (where 'Accept-Language: HU' is selected to be kept):
aResponse().withHeader("Accept-Language", "HU").withHeader("Accept-Language", "JA");
//to:
aResponse().withHeader("Accept-Language", "HU");
//From:
aResponse()
.withHeader("Accept-Language", "HU")
.withHeader("Content-Length", "1024")
.withBodyFile("filename")
.withHeader("Accept-Language", "JA") //<- selected to be kept
.withHeader("Accept-Language", "RU");
//to:
aResponse()
.withHeader("Content-Length", "1024")
.withBodyFile("filename")
.withHeader("Accept-Language", "JA");
with*Body*
When the response body is defined multiple ways (or even multiple times the same way), it can be confusing which body is actually applied, and these definitions will override each other.
This inspection supports the following response body definition calls: withJsonBody()
, withBody()
, withBase64Body()
, withBodyFile()
.
Regardless of if multiple of the same method, or different methods are called, they are all reported and highlighted if duplicates are found.
stubFor(get("/").willReturn(aResponse().withBodyFile("path/to/a_file.json").withBody("{\"aJson\": \"string\"}")));
The related quick fix removes duplicate calls, keeping the call that the user invokes the quick fix on:
//From (where the first 'withBodyFile()' call is selected to be kept):
aResponse().withBodyFile("path/to/a_file.json").withBodyFile("path/to/another_file.json");
//to:
aResponse().withBodyFile("path/to/a_file.json");
//From:
aResponse()
.withBodyFile("path/to/a_file.json")
.withBase64Body("eyJhSnNvbiI6ICJzdHJpbmcifQ==")
.withHeader("Accept-Language", "HU")
.withBody("{}") //<- selected to be kept
.withFixedDelay(200)
.withJsonBody(jsonBody);
//to:
aResponse()
.withHeader("Accept-Language", "HU")
.withBody("{}")
.withFixedDelay(200);
withStatus
This part of the inspection detects withStatus()
calls that are specified in call chains in which the first call in the chain already configures the status code behind the scenes. It also provides a quick fix to remove the redundant withStatus()
call.
//Chains starting with 'WireMock.aResponse()' are not analyzed,
// since this is probably the most common way of starting a response definition,
// regardless there is a default status value, 200, set when calling only 'aResponse()'.
stubFor(post("/").willReturn(aResponse()));
stubFor(post("/").willReturn(aResponse().withStatus(301)));
//The following ones are all reported (the list is not comprehensive):
stubFor(get("/").willReturn(WireMock.ok().withStatus(200)));
stubFor(get("/").willReturn(WireMock.okForContentType("application/json", "{}").withHeader("Location", "location").withStatus(500)));
stubFor(get("/").willReturn(WireMock.status(301).withStatus(400).withHeader("Location", "location")));
MappingBuilder
withHeader
//From:
stubFor(get(urlEqualTo("/path"))
.withHeader("Accept-Language", equalTo("HU")) //<- selected to be kept
.withHeader("Accept-Language", equalTo("JA"))
.willReturn(aResponse()));
//to:
stubFor(get(urlEqualTo("/path"))
.withHeader("Accept-Language", equalTo("HU"))
.willReturn(aResponse()));
withQueryParam
//From:
stubFor(get(urlEqualTo("/path"))
.withQueryParam("language", equalTo("HU")) //<- selected to be kept
.withQueryParam("language", equalTo("JA"))
.willReturn(aResponse()));
//to:
stubFor(get(urlEqualTo("/path"))
.withQueryParam("language", equalTo("HU"))
.willReturn(aResponse()));
RequestPatternBuilder
Duplicate withHeader
//From (where 'Accept-Language: HU' is selected to be kept):
verify(getRequestedFor(urlEqualTo("/")).withHeader("Accept-Language", equalTo("HU")).withPort(8080).withHeader("Accept-Language", equalTo("JA")));
//to:
verify(getRequestedFor(urlEqualTo("/")).withHeader("Accept-Language", equalTo("HU")).withPort(8080));
Duplicate withQueryParam
//From (where 'language=HU' is selected to be kept):
verify(getRequestedFor(urlEqualTo("/")).withQueryParam("language", equalTo("HU")).withPort(8080).withQueryParam("language", equalTo("JA")));
//to:
verify(getRequestedFor(urlEqualTo("/")).withQueryParam("language", equalTo("HU")).withPort(8080));
Absent withHeader and withQueryParam
withHeader()
and withQueryParam()
both have a convenience method when they parameterized with either the WireMock.absent()
or the MultiValuePattern.absent()
pattern matcher. With their dedicated quick fixes, they can be simplified to withoutHeader()
and withoutQueryParam()
, respectively.
//From:
verify(getRequestedFor(urlEqualTo("/")).withHeader("language", WireMock.absent()))
verify(getRequestedFor(urlEqualTo("/")).withHeader("language", MultiValuePattern.absent()));
//to:
verify(getRequestedFor(urlEqualTo("/")).withoutHeader("language"));
//From:
verify(getRequestedFor(urlEqualTo("/")).withQueryParam("language", WireMock.absent()))
verify(getRequestedFor(urlEqualTo("/")).withQueryParam("language", MultiValuePattern.absent()));
//to:
verify(getRequestedFor(urlEqualTo("/")).withoutQueryParam("language"));
Duplicate configuration (JSON DSL)
This inspection reports duplicate configuration in response
and request
properties in mapping files.
Detected property type | Supported property names | Since | Comments |
---|---|---|---|
body | body , base64Body , bodyFileName , jsonBody | ||
url | url , urlPattern , urlPath , urlPathPattern | According to the WireMock JSON schemas, in terms of the request url definitions: “Only one of url, urlPattern, urlPath or urlPathPattern may be specified” |
Regardless if multiple of the same property, or different properties are specified, they are all reported and highlighted if duplicates are found.
Also, the related quick fix removes duplicate properties, keeping the property that the user invokes the quick fix on.
Example – body:
//From (where 'body' is selected to be kept):
{
"request": { ... },
"response": {
"status": 200,
"bodyFileName": "a/response_body.json",
"body": "{\"aJson\": \"string\"}",
"base64Body": "eyJhSnNvbiI6ICJzdHJpbmcifQ=="
}
}
//to:
{
"request": { ... },
"response": {
"status": 200,
"body": "{\"aJson\": \"string\"}"
}
}
Example – url:
//From (where 'urlPattern' is selected to be kept):
{
"request": {
"method": "GET",
"url": "/url",
"urlPattern": "/url-pa.*ern"
},
"response": { ... }
}
//to:
{
"request": {
"method": "GET",
"urlPattern": "/url-pa.*ern"
},
"response": { ... }
}
Code completion for Content-Type header values
This completion provides suggestions for the Content-Type header values in multiple places in the Java and JSON DSL.
Java DSL
It is available in header value parameters of ResponseDefinitionBuilder.withHeader()
, MappingsBuilder.withHeader()
and WebhookDefinition.withHeader()
calls when the header name is Content-Type.
- Regarding
StringValuePattern
arguments, the following matchers are supported:equalTo()
,equalToIgnoreCase()
,containing()
,matching()
,notMatching()
. - Completion in
matching()
andnotMatching()
is not available by default. For them to work, the language injection called WireMock String Value Matchers must be turned off inSettings > Editor > Language Injections
.
ResponseDefinitionBuilder | MappingsBuilder |
JSON DSL
It is available in header value string literals within request.headers.Content-Type
, response.headers.Content-Type
and serveEventListeners.parameters.headers.Content-Type
in stub-mapping files.
- Regarding matchers, the following ones are supported:
equalTo
,contains
,doesNotContain
, including theand
andor
combined matchers. matches
anddoesNotMatch
matchers are not supported because those values are injected with RegExp language, and that injection cannot be turned off at the moment.
Code completion of GRPC status name header values
This completion provides suggestions for the response.headers.grpc-status-name
header values in the JSON DSL.
It displays the list of status enum entries from WireMockGrpc.Status
.
The completion is available only when the wiremock-grpc-extension is configured in the project.
Validation of GRPC status name header values
This inspection validates the value specified in the response.headers.grpc-status-name
JSON property against
the valid status values in the WireMockGrpc.Status
enum.
The inspection is executed only when the wiremock-grpc-extension is configured in the project.