diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c70aa783..7eed39e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,16 +11,16 @@ on: jobs: # Build job build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v3 with: submodules: "true" - name: Setup Ruby - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + uses: ruby/setup-ruby@4a9ddd6f338a97768b8006bf671dfbad383215f4 # v1.207.0 with: - ruby-version: '2.7.6' # Not needed with a .ruby-version file + ruby-version: '2.7.2' # Not needed with a .ruby-version file bundler: '2.4.10' bundler-cache: false # runs 'bundle install' and caches installed gems automatically cache-version: 3 # Increment this number if you need to re-download cached gems @@ -28,7 +28,7 @@ jobs: id: pages uses: actions/configure-pages@v3 - name: Update RubyGems and Co - run: gem update --system + run: gem update - name: Install Ruby Bundles run: bundle install - name: Build with Jekyll @@ -54,4 +54,4 @@ jobs: # enforce_https: false - name: Upload artifact # Automatically uploads an artifact from the './_site' directory by default - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index adfb4b90..e9403ad1 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -2,8 +2,8 @@ name: Deploy Jekyll site to Pages on: # Runs on pushes targeting the default branch - push: - branches: ["main"] + # push: + # branches: ["main"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -30,7 +30,7 @@ jobs: with: submodules: "true" - name: Setup Ruby - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + uses: ruby/setup-ruby@4a9ddd6f338a97768b8006bf671dfbad383215f4 # v1.207.0 with: ruby-version: '2.7.6' # Not needed with a .ruby-version file bundler: '2.4.10' @@ -40,7 +40,7 @@ jobs: id: pages uses: actions/configure-pages@v3 - name: Update RubyGems and Co - run: gem update --system + run: gem update - name: Install Ruby Bundles run: bundle install - name: Build with Jekyll @@ -76,7 +76,7 @@ jobs: # enforce_https: false - name: Upload artifact # Automatically uploads an artifact from the './_site' directory by default - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 # Deployment job deploy: @@ -88,4 +88,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/updatecli.yaml b/.github/workflows/updatecli.yaml deleted file mode 100644 index f3aafbcf..00000000 --- a/.github/workflows/updatecli.yaml +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: "Updatecli" - -on: - workflow_dispatch: - schedule: - # * is a special character in YAML so you have to quote this string - # trigger every hour the following pipeline - - cron: '0 * * * *' - -permissions: - contents: write - pull-requests: write - -jobs: - updatecli: - runs-on: ubuntu-latest - # Ensure we only run the following pipeline from main branch - # with a GITHUB_TOKEN that has "contents: write" permission - if: github.ref == 'refs/heads/main' - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install Updatecli Binary - uses: updatecli/updatecli-action@v2 - - - name: Run Updatecli in enforce mode - run: "updatecli apply --config .github/updatecli/updatecli.d" - env: - GITHUB_ACTOR: ${{ github.actor }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Gemfile.lock b/Gemfile.lock index b968d007..dae010b3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,11 +108,11 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) - wdm (0.1.1) webrick (1.8.1) PLATFORMS arm64-darwin-21 + arm64-darwin-23 x64-mingw32 x86_64-linux @@ -126,7 +126,6 @@ DEPENDENCIES jemoji json (~> 1.8.6) nokogiri (>= 1.7.2) - wdm (~> 0.1.0) webrick (~> 1.7) BUNDLED WITH diff --git a/_config.yml b/_config.yml index 0dc9f33e..c70f2a6e 100644 --- a/_config.yml +++ b/_config.yml @@ -234,11 +234,13 @@ compress_html: ignore: envs: development -wiremock_version: 3.3.1 +wiremock_version: 3.13.1 +wiremock_4_version: 4.0.0-beta.10 wiremock_baseline: 3.x pageEditPrefix: https://github.com/wiremock/wiremock.org/edit/main/ -grpc_extension_version: 0.4.0 +grpc_extension_version: 0.11.0 +spring_boot_integration_version: 3.10.0 community_slack: join_url: https://slack.wiremock.org/ diff --git a/_data/doc-categories.yml b/_data/doc-categories.yml index 2e6a15f4..95962215 100644 --- a/_data/doc-categories.yml +++ b/_data/doc-categories.yml @@ -22,11 +22,13 @@ java: pages: - junit-jupiter - junit-extensions - - spring-boot + - spring-boot-integration - java-usage - configuration - running-without-http-server + - jetty-12 - android + - pact stubbing-and-verifying: title: Stubbing & Verifying @@ -40,8 +42,8 @@ stubbing-and-verifying: - proxying - verifying -templating: - title: Templating +library: + title: Mock API Template Library link: /docs/mock-api-templates pages: - mock-api-templates @@ -59,8 +61,14 @@ protocols: - webhooks-and-callbacks - grpc - solutions/graphql + - jwt - https +integrations: + title: Integrations + pages: + - spring-boot + configuration: title: Advanced use-cases pages: @@ -85,6 +93,10 @@ extensibility: - extensibility/adding-mappings-loader - stub-metadata +v4: + title: WireMock v4 + link: /docs/v4/ + reference: title: Reference pages: diff --git a/_docs/configuration.md b/_docs/configuration.md index accf16a5..677e2a2a 100644 --- a/_docs/configuration.md +++ b/_docs/configuration.md @@ -5,6 +5,8 @@ meta_title: Configuring WireMock in Java | WireMock description: Configuring WireMock progammatically in Java. --- +
Centralize and scale your API mocks with WireMock Cloud.
+ Both `WireMockServer` and the `WireMockRule` take a configuration builder as the parameter to their constructor e.g. ```java @@ -98,6 +100,20 @@ WireMock uses the trust store for three purposes: 3. As a proxy, WireMock will trust a target server if it presents a public certificate in this trust store + +## HTTP/2 configuration + +HTTP/2 can be disabled separately for plain text (HTTP) and TLS (HTTPS): + +```java +// Disable HTTP/2 over HTTP +.http2PlainDisabled(true); + +// Disable HTTP/2 over HTTPS +.http2TlsDisabled(true); +``` + + ## Proxy settings ```java @@ -109,6 +125,9 @@ WireMock uses the trust store for three purposes: // Send the Host header in the original request onwards to the system being proxied to .preserveHostHeader(false) + +// As of WireMock `3.7.0`, when in proxy mode, this option will transfer the original `User-Agent` header from the client to the proxied service. +.preserveUserAgentProxyHeader(true) // Override the Host header sent when reverse proxying to another system (this and the previous parameter are mutually exclusive) .proxyHostHeader("my.otherdomain.com") @@ -130,6 +149,11 @@ WireMock uses the trust store for three purposes: // The type of the CA key store .caKeystoreType("JKS") + +// Which proxy encodings to proxy through to the target if the request contains an Accept-Encoding header +// By default this is null, which means the header is sent to the target unchanged +// If there is an Accept-Encoding header on the request, and it does not contain any of the supported proxy encodings, the header is not sent to the target. +.withSupportedProxyEncodings("gzip", "deflate") ``` ## File locations @@ -156,6 +180,16 @@ The request journal records requests received by WireMock. It is required by the .maxRequestJournalEntries(Optional.of(100)) ``` +## Template Cache + +When response templating is enabled, compiled template fragments are cached to improve performance. This setting allows +you to configure the maximum number of entries to allow in the cache. As of WireMock `3.7.0`, this defaults to 1000 +cache entries. Before WireMock `3.7.0` the default was unlimited + +```java +.withMaxTemplateCacheEntries(100) +``` + ## Notification (logging) WireMock wraps all logging in its own `Notifier` interface. It ships with no-op, Slf4j and console (stdout) implementations. @@ -273,3 +307,26 @@ To output all raw traffic to console use `ConsoleNotifyingWiremockNetworkTraffic If you would like to collect the traffic and for example add it to your acceptance test's output, you can use the `CollectingNetworkTrafficListener`. + +## HTTP Client +If you want to increase the proxying performance of WireMock you can enable connection reuse and increase the maximum number of connections: + +```java +// Maximum connections for Http Client +.maxHttpClientConnections(1000); +//Disable http connection reuse, `false` to enable +.disableConnectionReuse(true) +``` + +## Webhook configuration + +The default webhook thread pool size is 10. This is more than enough for normal mocking with callbacks but if you are +running performance tests using WireMock with callbacks, you might need to tweak the size of the threadpool used to +process webhook requests. This option is available as of WireMock version `3.13.0` + +```java +// The number of threads created for processing webhook requests. Defaults to 10 +.withWebhookThreadPoolSize(100) + +``` + diff --git a/_docs/download-and-installation.md b/_docs/download-and-installation.md index 6c7fa81a..cd7a95f6 100644 --- a/_docs/download-and-installation.md +++ b/_docs/download-and-installation.md @@ -4,17 +4,18 @@ title: Download and Installation meta_title: "How to Download and Install WireMock" toc_rank: 13 description: > - WireMock is available as a standalone service (for Docker of Java), Java library - and integrations for modern languages and technology stacks. -redirect_from: - - "/download.html" - - "/download/" - - "/downloads.html" - - "/downloads/" - - "/docs/download.html" - - "/docs/download/" + WireMock is available as a standalone service (for Docker of Java), Java library + and integrations for modern languages and technology stacks. +redirect_from: + - "/download.html" + - "/download/" + - "/downloads.html" + - "/downloads/" + - "/docs/download.html" + - "/docs/download/" --- +
To create publicly hosted mock APIs without anything to install, learn more about WireMock Cloud.
## Download options @@ -24,13 +25,17 @@ WireMock plus all its dependencies. Most of the standalone JAR's dependencies are shaded i.e. they are hidden in alternative packages. This allows WireMock to be used in projects with conflicting versions of its dependencies. The standalone JAR is also runnable (see [Running as a Standalone Process](../running-standalone/)). -## Test dependencies +WireMock currently has two releases available. The `3.x` release (below) and the new `4.x` beta releases. + +## 3.x Release Downloads + +### Test dependencies
{% include downloads.html %}
-## Standalone Service +### Standalone Service Run the following in a terminal: @@ -58,7 +63,7 @@ docker run -it --rm -p 8080:8080 --name wiremock \ {% endcodetab %} -{% codetab Gradle Groovy %} +{% codetab Gradle %} ```groovy testImplementation "org.wiremock:wiremock-standalone:{{ site.wiremock_version }}" @@ -75,3 +80,55 @@ Learn more in the [Docker guide](../docker). If you want to run WireMock as a standalone process you can download the standalone JAR from here + +## 4.x Beta Release Downloads + +The `4.x` release of WireMock is currently in beta. These releases are under active development and we recommend you try it out. We would love +to hear your feedback over on the community slack - [https://slack.wiremock.org/](https://slack.wiremock.org/) + +We have given these releases a beta label due to the fact that as we move forwards with the `4.x` release there **will be +breaking changes**. These are the current updates to the `4.x` release: + +* Java 17 is now the baseline java version +* Jetty 12 is shipped by default so there is no longer a specific jetty 12 release of `4.x` and Jetty 11 is no longer supported + +### Test dependencies + +
+ {% include downloads-v4.html %} +
+ +### Standalone Service + +Run the following in a terminal: + +{% codetabs %} + +{% codetab Maven %} + +```xml + + org.wiremock + wiremock-standalone + {{ site.wiremock_4_version }} + test + +``` + +{% endcodetab %} + +{% codetab Gradle %} + +```groovy +testImplementation "org.wiremock:wiremock-standalone:{{ site.wiremock_4_version }}" +``` + +{% endcodetab %} + +{% endcodetabs %} + +### Direct download + +If you want to run WireMock as a standalone process you can +download the standalone JAR from +here diff --git a/_docs/extending-wiremock.md b/_docs/extending-wiremock.md index bebd4462..be464785 100644 --- a/_docs/extending-wiremock.md +++ b/_docs/extending-wiremock.md @@ -7,6 +7,8 @@ redirect_from: "/extending-wiremock.html" description: You can register the extension programmatically via its class name, class or an instance --- +
Chaos testing, RBAC, dynamic state and more with WireMock Cloud.
+ WireMock can be customised via a variety of extension points. Each extension point is defined by an interface that extends from `Extension` and extension implementations are loaded at startup time. @@ -26,6 +28,10 @@ At present, the following extension interfaces are available: The interfaces in this list ending with `V2` supercede deprecated equivalents with an older, more restrictive interface. Additionally `ServeEventListener` deprecates `PostServeAction`. +As of WireMock version `3.6.0`, the `Extension` interface has two new lifecycle methods called `start()` and `stop()`. +The `start()` method is called on each extension when the WireMock server first starts (just before the stub mappings +are loaded) and the `stop()` method is called when the server is stopped. This allows extensions to perform any +initialisation or cleanup tasks. ## Registering Extensions diff --git a/_docs/extensibility/adding-template-model-data.md b/_docs/extensibility/adding-template-model-data.md index 7dc0f456..c4bb1be7 100644 --- a/_docs/extensibility/adding-template-model-data.md +++ b/_docs/extensibility/adding-template-model-data.md @@ -5,7 +5,7 @@ meta_title: Adding Template Model Data description: Adding extra elements to the template model during request processing --- -Extensions that implement the `TemplateHelperProviderExtension` interface provide additional Handlebars helpers to the templating system: +Extensions that implement the `TemplateModelDataProviderExtension` interface provide additional model elements to the templating system: ```java new WireMockServer(.extensions( diff --git a/_docs/extensibility/filtering-requests.md b/_docs/extensibility/filtering-requests.md index 3bda7a62..2d3be6a4 100644 --- a/_docs/extensibility/filtering-requests.md +++ b/_docs/extensibility/filtering-requests.md @@ -5,6 +5,8 @@ meta_title: Filtering and Modifying Requests description: Filtering and modifying requests via extensions --- +
If you need to protect your mock APIs with Enterprise-grade security options, learn about WireMock Cloud.
+ Requests to both stubs and the admin API can be intercepted and either modified or halted with an immediate response. This supports a number of use cases including: authentication, URL rewriting and request header injection. diff --git a/_docs/getting-started.md b/_docs/getting-started.md index 76c09df5..8b3bbbe3 100644 --- a/_docs/getting-started.md +++ b/_docs/getting-started.md @@ -18,6 +18,7 @@ At the moment, we provide the following quick starts for beginners: - [API Mocking with Java and JUnit 4](../quickstart/java-junit) - [Downloading and Installing WireMock](../download-and-installation) +- [Using WireMock with Jetty 12](../jetty-12) diff --git a/_docs/grpc.md b/_docs/grpc.md index 7d264bc3..a45adb2c 100644 --- a/_docs/grpc.md +++ b/_docs/grpc.md @@ -11,6 +11,8 @@ redirect_from: - "/grpc/" --- +
WireMock Cloud can transform your gRPC API development, learn more about WireMock Cloud.
+ WireMock 3.2.0+ supports mocking of gRPC services via the [WireMock extension for gRPC](https://github.com/wiremock/wiremock-grpc-extension). @@ -45,9 +47,9 @@ Maven: ``` -Create a root directory for WireMock e.g. `src/test/resources/wiremock` and create a subdirectory under this named `grpc`. +Create a root directory for WireMock, typically `src/test/resources/wiremock`, and create a subdirectory in it named `grpc`. -Put the descriptor files generated by `protoc` from your `.proto` files into the `grpc` directory. +Copy the descriptor files generated by `protoc` from your `.proto` files into the `grpc` subdirectory. Initialise WireMock server with the extension enabled and the root directory set to the path created in the previous steps: @@ -72,7 +74,7 @@ WireMockGrpcService mockGreetingService = ### Stubbing via JSON matching + responses -To specify request criteria and response data via JSON: +To specify request criteria and response data using JSON: ```java mockGreetingService.stubFor( @@ -81,8 +83,9 @@ mockGreetingService.stubFor( .willReturn(json("{ "greeting": "Hi Tom from JSON" }"))); ``` +Or, with a templated response: -Or with a templated response: +{% raw %} ```java mockGreetingService.stubFor( @@ -93,6 +96,8 @@ mockGreetingService.stubFor( "{ \"greeting\": \"Hello {{jsonPath request.body '$.name'}}\" }"))); ``` +{% endraw %} + ### Stubbing via Java message objects Matching and stubbing in the Java DSL can also be specified using the Java classes generated by `protoc`: @@ -126,13 +131,13 @@ For a more complete set of examples, see the [Java demo project](https://github. ### Setup -Download the standalone JAR at version 3.2.0 or above +Download the standalone JAR at version 3.2.0 or above and the gRPC extension JAR into your working directory. -Create a WireMock data directory with a subdirectory for stub mappings and one for descriptor files: +Create a WireMock data directory with two subdirectories; one for stub mappings, and another for descriptor files: ```bash -mkdir -p wiremock/mappings wiremock/grpc +mkdir -p wiremock wiremock/mappings wiremock/grpc ``` Compile your proto files into descriptors: @@ -141,12 +146,12 @@ Compile your proto files into descriptors: protoc --descriptor_set_out wiremock/grpc/services.dsc ExampleServices.proto ``` -Run WireMock with both on the classpath: +Run WireMock, with both directories you just created on the classpath: ```bash -java -cp wiremock-standalone-{{ site.wiremock_version }}.jar:wiremock-grpc-extension-standalone-{{ site.grpc_extension_version }}.jar \ +java -cp wiremock-standalone-{{ versions.wiremock_version }}.jar:wiremock-grpc-extension-standalone-{{ versions.grpc_extension_version }}.jar \ wiremock.Run \ - --root-dir wiremock-data + --root-dir wiremock ``` ### Stubbing @@ -172,6 +177,10 @@ gRPC stubs are defined using WireMock's standard JSON format. Requests should al } ``` +## Reloading gRPC descriptor files + +If you plan to update your gRPC descriptor files at runtime, you can inform WireMock to reload all file descriptors via a POST to the admin API endpoint `/__admin/ext/grpc/reset`. + ## More Demos -For more see the [standalone demo project](https://github.com/wiremock/wiremock-grpc-demos/tree/main/standalone). \ No newline at end of file +For more see the [standalone demo project](https://github.com/wiremock/wiremock-grpc-demos/tree/main/standalone). diff --git a/_docs/https.md b/_docs/https.md index 289c16c4..391940bd 100644 --- a/_docs/https.md +++ b/_docs/https.md @@ -1,7 +1,7 @@ --- layout: docs -title: Serving HTTPs -meta_title: Using WireMock with HTTPs using self-signed or custom certificates | WireMock +title: Serving HTTPS +meta_title: Using WireMock with HTTPS using self-signed or custom certificates | WireMock redirect_from: "/https.html" description: WireMock can optionally accept requests over HTTPS. By default it will serve its own self-signed TLS certificate. --- @@ -76,4 +76,4 @@ HTTP port with a client that's expecting HTTPS (i.e. has `https://` in the URL). `org.apache.hc.core5.http.NoHttpResponseException: The target server failed to respond`: Could mean you've tried to connect to the HTTPS port with a client expecting HTTP. -`javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target`: You are using WireMock's default (self-signed) TLS certificate or another certificate that isn't signed by a CA. In this case you need to specifically configure your HTTP client to trust the certificate being presented, or to trust all certificates. Here is an example of [how to do this with the Apache HTTP client](https://github.com/tomakehurst/wiremock/blob/{{ site.wiremock_version }}/src/main/java/com/github/tomakehurst/wiremock/http/HttpClientFactory.java). +`javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target`: You are using WireMock's default (self-signed) TLS certificate or another certificate that isn't signed by a CA. In this case you need to specifically configure your HTTP client to trust the certificate being presented, or to trust all certificates. Here is an example of [how to do this with the Apache HTTP client](https://github.com/wiremock/wiremock/blob/3.4.2/src/main/java/com/github/tomakehurst/wiremock/http/HttpClientFactory.java#L207). diff --git a/_docs/index.html b/_docs/index.html index 087b0d39..93b89744 100644 --- a/_docs/index.html +++ b/_docs/index.html @@ -113,7 +113,7 @@

Distributions

By technology Python - + Spring Boot diff --git a/_docs/jetty-12.md b/_docs/jetty-12.md new file mode 100644 index 00000000..91769fe8 --- /dev/null +++ b/_docs/jetty-12.md @@ -0,0 +1,62 @@ +--- +layout: docs +title: "Using WireMock with Jetty 12" +meta_title: Using WireMock with Jetty 12 | WireMock +description: WireMock ships with Jetty 11 by default but fully supports Jetty 12 as well. +--- + +WireMock ships with Jetty 11 by default but fully supports Jetty 12 as well with a new module `wiremock-jetty12`. In this tutorial we are going to see how Wiremock could be configured to use Jetty 12. + +## Prerequisites + +- Java 17 +- Maven or Gradle, recent versions +- A Java project, based on Maven or Gradle + +## Add WireMock Dependency to your project + +{% codetabs %} + +{% codetab Maven %} + +```xml + + org.wiremock + wiremock-jetty12 + {{ site.wiremock_version }} + test + +``` + +{% endcodetab %} + +{% codetab Gradle %} + +```groovy +testImplementation "org.wiremock:wiremock-jetty12:{{ site.wiremock_version }}" +``` + +{% endcodetab %} + +{% endcodetabs %} + +## Limitations + +There are few limitations that usage of Jetty 12 is imposing with respect to stubbing behavior. + +- status message will not be returned to the client even if set by the stub explicitly + ```java + stubFor(get("/my/resource") + .willReturn(status(400) + .withStatusMessage("ERROR"))); + + URI uri = URI.create(wireMockRule.url("/my/resource")); + HttpURLConnection connection = (HttpURLConnection) uri.toURL ().openConnection (); + connection.setRequestMethod ("GET"); + + assertThat(connection.getResponseCode()).isEqualTo(400); + assertThat(connection.getResponseMessage()).isEqualTo("Bad Request"); /* the status message is not returned */ + ``` +- when using multipart form data, the body is not decoded into plain text in case of `base64` (or other encodings) + +- serving files from configured file locations always ends up with redirect when folder (without trailing `/`) is requested diff --git a/_docs/junit-jupiter.md b/_docs/junit-jupiter.md index 2645fb75..3ab4933a 100644 --- a/_docs/junit-jupiter.md +++ b/_docs/junit-jupiter.md @@ -79,6 +79,18 @@ public class HttpsFixedPortDeclarativeWireMockTest { } ``` +### Enabling Extension Scanning + +When [extending WireMock via service loading](extending-wiremock.md#extension-registration-via-service-loading), it may +be helpful to have WireMock scan for extensions automatically via the `extensionScanningEnabled` parameter. + +```java +@WireMockTest(extensionScanningEnabled = true) +public class ExtensionScanningDeclarativeWireMockTest { + ... +} +``` + ## Advanced usage - programmatic Invoking the extension programmatically with `@RegisterExtension` allows you to run any number of WireMock instances and provides full control @@ -96,7 +108,13 @@ public class ProgrammaticWireMockTest { static WireMockExtension wm2 = WireMockExtension.newInstance() .options(wireMockConfig() .dynamicPort() - .extensions(new ResponseTemplateTransformer(true))) + .extensions(new ResponseTemplateTransformer( + getTemplateEngine(), + options.getResponseTemplatingGlobal(), + getFiles(), + templateModelProviders + ) + ) .build(); @Test @@ -119,7 +137,7 @@ public class ProgrammaticWireMockTest { ### Static vs. instance In the above example, as with the declarative form, each WireMock server will be started before the first test method in the test class and stopped after the -last test method has completed, with a call to reset before each test method. +last test method has completed, and by default, with a call to reset before each test method. However, if the extension fields are declared at the instance scope (without the `static` modifier) each WireMock server will be created and started before each test method and stopped after the end of the test method. @@ -153,6 +171,13 @@ public class AutomaticStaticDslConfigTest { } ``` +## Resetting before each test method +By default WireMock will be reset before each tests method. This will reset the stubs and any requests that have been +made. + +Most of the time this is the desired behaviour but this behavior can be changed by calling `.resetOnEachTest(false)` on +the extension builder when using the programmatic form. This option is available as of WireMock version `3.13.0` + ## Unmatched request behaviour By default, in either the declarative or programmatic form, if the WireMock instance receives unmatched requests during a diff --git a/_docs/jwt.md b/_docs/jwt.md new file mode 100644 index 00000000..1bd3e9c0 --- /dev/null +++ b/_docs/jwt.md @@ -0,0 +1,10 @@ +--- +layout: docs +title: JSON Web Tokens (JWT) +meta_title: JSON Web Tokens +description: Generating JWTs and JWKSs +--- + +The JWT extension provides support for generating JWTs and corresponding JSON Web Key Sets (JWKS) in stub responses via response templating. + +For details see [the project's README](https://github.com/wiremock/wiremock-jwt-extension/blob/main/README.md). \ No newline at end of file diff --git a/_docs/mock-api-templates.md b/_docs/mock-api-templates.md index aa0dbc56..3229a7d6 100644 --- a/_docs/mock-api-templates.md +++ b/_docs/mock-api-templates.md @@ -8,6 +8,8 @@ description: > that can be used with both WireMock or WireMock Cloud. --- +
To easily share templates across teams or choose from thousands of popular 3rd party templates, learn more about WireMock Cloud.
+ The [library.wiremock.org](https://library.wiremock.org) site provides a catalog of API Templates that can be used with both [WireMock](https://wiremock.org/) or [WireMock Cloud](https://wiremock.io). diff --git a/_docs/proxying.md b/_docs/proxying.md index 4bd01be1..0ff4f76c 100644 --- a/_docs/proxying.md +++ b/_docs/proxying.md @@ -7,6 +7,8 @@ redirect_from: "/proxying.html" description: Proxy responses are defined in exactly the same manner as stubs, meaning that the same request matching criteria can be used. --- +
Create stubs and scenarios with WireMock Cloud's intuitive editor and share with your team.
+ WireMock has the ability to selectively proxy requests through to other hosts. This supports a proxy/intercept setup where requests are by default proxied to another (possibly real, live) service, but where @@ -102,7 +104,7 @@ the request to the destination: stubFor(get(urlMatching(".*")) .willReturn(aResponse() .proxiedFrom("http://otherhost.com") - .withAdditionalRequestHeader("User-Agent", "Mozilla/5.0 (iPhone; U; CPU iPhone)")); + .withAdditionalRequestHeader("User-Agent", "Mozilla/5.0 (iPhone; U; CPU iPhone)"))); ``` or @@ -124,6 +126,35 @@ or You can also add response headers via the same method as for non-proxy responses (see [Stubbing](../stubbing/)). +# Remove headers + +It is possible to configure the proxy to remove headers before forwarding the reques to the destination +([additional headers](#additional-headers) matching the removed headers will still be added). + +```java +stubFor(get(urlMatching(".*")) + .willReturn(aResponse() + .proxiedFrom("http://otherhost.com") + .withRemoveRequestHeader("User-Agent"))); +``` + +or + +```json +{ + "request": { + "method": "GET", + "urlPattern": ".*" + }, + "response": { + "proxyBaseUrl": "http://otherhost.com", + "removeProxyRequestHeaders": [ + "User-Agent" + ] + } +} +``` + ## Standalone shortcut It is possible to start the standalone running with the catch-all stub diff --git a/_docs/quickstart/java-junit.md b/_docs/quickstart/java-junit.md index d9bc384a..cf1ef2ed 100644 --- a/_docs/quickstart/java-junit.md +++ b/_docs/quickstart/java-junit.md @@ -5,6 +5,8 @@ meta_title: "API Mocking QuickStart with Java and JUnit 4 | WireMock" description: "Shows how to write your API Client first test with WireMock and JUnit" --- +
Centralize and scale your API mocks with WireMock Cloud.
+ In this guide we will write an API Unit test with WireMock and JUnit 4. ## Prerequisites @@ -40,14 +42,14 @@ like [Apache HttpClient](https://hc.apache.org/httpcomponents-client-5.2.x/#). org.assertj assertj-core - 3.24.2 + 3.26.3 test ``` {% endcodetab %} -{% codetab Gradle Groovy %} +{% codetab Gradle %} ```groovy testImplementation "org.wiremock:wiremock:{{ site.wiremock_version }}" @@ -100,7 +102,7 @@ public void exampleTest() { // Setup HTTP POST request (with HTTP Client embedded in Java 11+) final HttpClient client = HttpClient.newBuilder().build(); final HttpRequest request = HttpRequest.newBuilder() - .uri(wiremockServer.getRequestURI("/my/resource")) + .uri(wiremockServer.url("/my/resource")) .header("Content-Type", "text/xml") .POST().build(); @@ -151,6 +153,7 @@ int httpsPort = wireMockRule.httpsPort(); ## Further reading - For more details on verifying requests and stubbing responses, see [Stubbing](../../stubbing) and [Verifying](../../verifying/) -- For more information on the JUnit rules see [The JUnit 4 Rule](../../junit-4/). +- For more information on the JUnit 4 rules see [The JUnit 4 Rule](../../junit-4/). +- For more information on the JUnit 5 Jupiter extension see [JUnit 5+ Jupiter](../../junit-jupiter/); for previous JUnit versions you can use [the JUnit 4 Rule](../../junit-extensions/). - For many more examples of JUnit tests check out the [WireMock's own acceptance tests](https://github.com/wiremock/wiremock/tree/master/src/test/java/com/github/tomakehurst/wiremock) diff --git a/_docs/request-matching.md b/_docs/request-matching.md index 73b06d58..33271cdc 100644 --- a/_docs/request-matching.md +++ b/_docs/request-matching.md @@ -6,6 +6,8 @@ toc_rank: 61 description: WireMock supports matching of requests to stubs and verification queries using the following attributes. --- +
If you need to protect your mock APIs with Enterprise-grade security options, learn about WireMock Cloud.
+ WireMock enables flexible definition of a [mock API](/) by supporting rich matching of incoming requests. Stub matching and verification queries can use the following request attributes: - URL @@ -17,14 +19,16 @@ WireMock enables flexible definition of a [mock API](/) by supporting rich match - Cookies - Request body - Multipart/form-data +- Client IP (as of WireMock version `3.13.0`) Here's an example showing all attributes being matched using WireMock's in-built match operators. It is also possible to write [custom matching logic](../extending-wiremock#custom-request-matchers) if you need more precise control: ## Request with XML Body -Code: +{% codetabs %} +{% codetab Java %} ```java stubFor(any(urlPathEqualTo("/everything")) .withHeader("Accept", containing("xml")) @@ -39,11 +43,12 @@ stubFor(any(urlPathEqualTo("/everything")) .withHeader("Content-Type", containing("charset")) .withBody(equalToJson("{}")) ) + .withClientIp(equalTo("127.0.0.1")) .willReturn(aResponse())); ``` +{% endcodetab %} -Configuration file: - +{% codetab JSON %} ```json { "request": { @@ -93,6 +98,9 @@ Configuration file: "basicAuthCredentials": { "username": "jeff@example.com", "password": "jeffteenjefftyjeff" + }, + "clientIp": { + "equalTo": "127.0.0.1" } }, "response": { @@ -100,15 +108,23 @@ Configuration file: } } ``` +{% endcodetab %} + +{% endcodetabs %} ## Request with Form Parameters +{% codetabs %} + +{% codetab Java %} ```java stubFor(post(urlPathEqualTo("/mock")) - .withFormParam("tool", equalTo("WireMock")) + .withFormParam("tool", equalTo("WireMock") ).willReturn(ok())); ``` +{% endcodetab %} +{% codetab JSON %} ```json { "request": { @@ -125,6 +141,9 @@ stubFor(post(urlPathEqualTo("/mock")) } } ``` +{% endcodetab %} + +{% endcodetabs %} The following sections describe each type of matching strategy in detail. @@ -136,14 +155,15 @@ It is usually preferable to match on path only if you want to match multiple que ### Equality matching on path and query -Java: +{% codetabs %} +{% codetab Java %} ```java urlEqualTo("/your/url?and=query") ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -153,17 +173,21 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Regex matching on path and query -Java: +{% codetabs %} +{% codetab Java %} ```java urlMatching("/your/([a-z]*)\\?and=query") ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -173,17 +197,21 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Equality matching on the path only -Java: +{% codetabs %} +{% codetab Java %} ```java urlPathEqualTo("/your/url") ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -193,17 +221,21 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Regex matching on the path only -Java: +{% codetabs %} +{% codetab Java %} ```java urlPathMatching("/your/([a-z]*)") ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -213,6 +245,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Path templates @@ -225,31 +260,37 @@ When the path template URL match type is used this enables To match any request URL that conforms to the path template, you can do the following. -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor( get(urlPathTemplate("/contacts/{contactId}/addresses/{addressId}")) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { "urlPathTemplate": "/contacts/{contactId}/addresses/{addressId}" - "method" : "GET", - + "method" : "GET" }, "response" : { "status" : 200 } } ``` +{% endcodetab %} + +{% endcodetabs %} To further constrain the match to specific values of the path variables you can add match clauses for some or all of the variables in the path expression. +{% codetabs %} + +{% codetab Java %} ```java stubFor( get(urlPathTemplate("/contacts/{contactId}/addresses/{addressId}")) @@ -257,9 +298,9 @@ stubFor( .withPathParam("addressId", equalTo("99876")) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request" : { @@ -279,7 +320,9 @@ JSON: } } ``` +{% endcodetab %} +{% endcodetabs %} ## Matching other attributes @@ -289,14 +332,15 @@ All request attributes other than the URL can be matched using the following set Deems a match if the entire attribute value equals the expected value. -Java: +{% codetabs %} +{% codetab Java %} ```java .withHeader("Content-Type", equalTo("application/json")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -311,19 +355,23 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Case-insensitive equality Deems a match if the entire attribute value equals the expected value, ignoring case. -Java: +{% codetabs %} +{% codetab Java %} ```java .withHeader("Content-Type", equalToIgnoreCase("application/json")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -339,13 +387,17 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Binary Equality Deems a match if the entire binary attribute value equals the expected value. Unlike the above equalTo operator, this compares byte arrays (or their equivalent base64 representation). -Java: +{% codetabs %} +{% codetab Java %} ```java // Specifying the expected value as a byte array .withRequestBody(binaryEqualTo(new byte[] { 1, 2, 3 })) @@ -353,9 +405,9 @@ Java: // Specifying the expected value as a base64 String .withRequestBody(binaryEqualTo("AQID")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -368,19 +420,23 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Substring (contains) Deems a match if the a portion of the attribute value equals the expected value. -Java: +{% codetabs %} +{% codetab Java %} ```java .withCookie("my_profile", containing("johnsmith@example.com")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -395,19 +451,23 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Negative substring (does not contain) Deems a match if the attribute value does not contain the expected value. -Java: +{% codetabs %} +{% codetab Java %} ```java .withCookie("my_profile", notContaining("johnsmith@example.com")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -422,19 +482,23 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Regular expression Deems a match if the entire attribute value matched the expected regular expression. -Java: +{% codetabs %} +{% codetab Java %} ```java .withQueryParam("search_term", matching("^(.*)wiremock([A-Za-z]+)$")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -449,17 +513,21 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} It is also possible to perform a negative match i.e. the match succeeds when the attribute value does not match the regex: -Java: +{% codetabs %} +{% codetab Java %} ```java .withQueryParam("search_term", notMatching("^(.*)wiremock([A-Za-z]+)$")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -474,19 +542,23 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### JSON equality Deems a match if the attribute (most likely the request body in practice) is valid JSON and is a semantic match for the expected value. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(equalToJson("{ \"total_results\": 4 }")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -499,9 +571,15 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} -JSON with string literal: +With string literal: +{% codetabs %} + +{% codetab JSON %} ```json { "request": { @@ -514,19 +592,23 @@ JSON with string literal: ... } ``` +{% endcodetab %} + +{% endcodetabs %} #### Less strict matching By default different array orderings and additional object attributes will trigger a non-match. However, both of these conditions can be disabled individually. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(equalToJson("{ \"total_results\": 4 }", true, true)) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -541,6 +623,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} #### Placeholders @@ -570,14 +655,15 @@ Deems a match if the attribute value is valid JSON and matches the [JSON Path](h Deems a match if the attribute value is present in the JSON. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingJsonPath("$.name")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -590,6 +676,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} Request body example: @@ -604,14 +693,15 @@ Request body example: Deems a match if the attribute value equals the expected value. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingJsonPath("$.things[?(@.name == 'RequiredThing')]")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -624,6 +714,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} Request body example: @@ -640,14 +733,15 @@ Request body example: Deems a match if the attribute value matches the regex expected value. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingJsonPath("$.things[?(@.name =~ /Required.*/i)]")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -660,6 +754,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} Request body example: @@ -677,14 +774,15 @@ Request body example: Deems a match if the attribute size matches the expected size. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingJsonPath("$[?(@.things.size() == 2)]")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -697,6 +795,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} Request body example: @@ -711,14 +812,15 @@ Request body example: The JSONPath matcher can be combined with another matcher, such that the value returned from the JSONPath query is evaluated against it: -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingJsonPath("$..todoItem", containing("wash"))) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -734,6 +836,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} Since WireMock's matching operators all work on strings, the value selected by the JSONPath expression will be coerced to a string before the match is evaluated. This true even if the returned value is an object or array. A benefit of this is that this allows a sub-document to be selected using JSONPath, then matched using the `equalToJson` operator. E.g. for the following request body: @@ -748,14 +853,17 @@ is an object or array. A benefit of this is that this allows a sub-document to b The following will match: +{% codetabs %} + +{% codetab Java %} ```java .withRequestBody(matchingJsonPath("$.outer", equalToJson("{ \n" + " \"inner\": 42 \n" + "}"))) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -771,6 +879,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### JSON schema @@ -778,8 +889,9 @@ Deems a match if the value conforms to the expected JSON schema. By default the [V202012](https://json-schema.org/draft/2020-12/schema){:target="{{site.data.misc.blank}}"} version of the JSON schema spec will be used, but this can be changed to one of `V4`, `V6`, `V7`, `V201909`, `V202012` via the `schemaVersion` parameter. -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor( post(urlPathEqualTo("/schema-match")) @@ -799,8 +911,47 @@ stubFor( "}")) .willReturn(ok())); ``` +{% endcodetab %} -JSON: +{% codetab JSON %} +(supported in 3.4+): +```json +{ + "request" : { + "urlPath" : "/schema-match", + "method" : "POST", + "bodyPatterns" : [ { + "matchesJsonSchema" : { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "schemaVersion" : "V202012" + } ] + }, + "response" : { + "status" : 200 + } +} +``` +{% endcodetab %} + +{% endcodetabs %} + +With string literal: + +{% codetabs %} + +{% codetab JSON %} ```json { @@ -817,20 +968,23 @@ JSON: } } ``` +{% endcodetab %} +{% endcodetabs %} ### XML equality Deems a match if the attribute value is valid XML and is semantically equal to the expected XML document. The underlying engine for determining XML equality is [XMLUnit](http://www.xmlunit.org/). -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(equalToXml("Hello")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -843,21 +997,25 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} #### Use of placeholders The XMLUnit [placeholders](https://github.com/xmlunit/user-guide/wiki/Placeholders) feature is supported in WireMock. For example, when comparing the XML documents, you can ignore some text nodes. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody( equalToXml("${xmlunit.ignore}Hello", true) ) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -871,13 +1029,17 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} When the actual request body is `123456Hello`, it will be deemed a match. -If the default placeholder delimiters `${` and `}` can not be used, you can specify custom delimiters (using regular expressions). For example +If the default placeholder delimiters `${` and `}` can not be used, you can specify custom delimiters (using regular expressions). For example: -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody( equalToXml("[[xmlunit.ignore]]Hello", @@ -887,9 +1049,9 @@ Java: ) ) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -905,13 +1067,17 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} #### Excluding specific types of comparison You can further tune how XML documents are compared for equality by disabling specific [XMLUnit comparison types](https://www.xmlunit.org/api/java/2.7.0/org/xmlunit/diff/ComparisonType.html). -Java: +{% codetabs %} +{% codetab Java %} ```java import static org.xmlunit.diff.ComparisonType.*; @@ -921,9 +1087,9 @@ import static org.xmlunit.diff.ComparisonType.*; .exemptingComparisons(NAMESPACE_URI, ELEMENT_TAG_NAME) ) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -937,6 +1103,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} The full list of comparison types used by default is as follows: @@ -944,6 +1113,7 @@ The full list of comparison types used by default is as follows: `SCHEMA_LOCATION` `NO_NAMESPACE_SCHEMA_LOCATION` `NODE_TYPE` +`NAMESPACE_PREFIX` `NAMESPACE_URI` `TEXT_VALUE` `PROCESSING_INSTRUCTION_TARGET` @@ -954,18 +1124,117 @@ The full list of comparison types used by default is as follows: `CHILD_LOOKUP` `ATTR_NAME_LOOKUP` +#### Same child nodes with different content +By default, WireMock takes into account an order of identical child nodes. Meaning if actual request has different order of same node on same level than stub it won't be matched. +As of WireMock version `3.7.0`, this can be changed by passing additional argument to the `equalToXml` method + +{% codetabs %} + +{% codetab Java %} +```java + .withRequestBody(equalToXml("" + + " 1" + + " 2" + + "",false,true)) +``` +{% endcodetab %} + +{% codetab JSON %} +```json +{ + "request": { + ... + "bodyPatterns" : [ { + "equalToXml" : "12", + "ignoreOrderOfSameNode": true + } ] + ... + }, + ... +} +``` +{% endcodetab %} + +{% endcodetabs %} + +This will make sure that stub above matches both of following requests: +```xml + + 2 + 1 + +``` +and +```xml + + 1 + 2 + +``` +If third argument is passed as `false` then first xml will not match the stub + +#### Namespace awareness + +To configure how [XML namespaces](https://www.w3schools.com/xml/xml_namespaces.asp) are handled, as of WireMock +`3.12.0`, the `namespaceAwareness` property can be set. + +{% codetabs %} + +{% codetab Java %} +```java + .withRequestBody(equalToXml("" + + " 1" + + " 2" + + "").withNamespaceAwareness(EqualToXmlPattern.NamespaceAwareness.STRICT)) +``` +{% endcodetab %} + +{% codetab JSON %} +```json +{ + "request": { + ... + "bodyPatterns" : [ { + "equalToXml" : "12", + "namespaceAwareness": "STRICT" + } ] + ... + }, + ... +} +``` +{% endcodetab %} + +{% endcodetabs %} + +The available options for namespace awareness behaviour are `STRICT`, `NONE` and `LEGACY`. + +`STRICT` adheres to strict XML namespace comparison. +Namespace prefixes must be bound to a namespace URI. +Namespace prefixes as well as namespace URIs must match (for both elements and attributes), unless explicitly excluded +by the `exemptedComparisons` parameter. + +`NONE` does not consider XML namespaces when reading and comparing XML documents. +Namespace prefixes do not need to be bound to a namespace URI and are not considered a separate part of an +element/attribute name (i.e. the entire element/attribute name must match, not just the local name, regardless of +the `exemptedComparisons` parameter). +`xmlns` namespaced attributes are treated no differently to any other attribute. + +`LEGACY` is not recommended and is only kept as an option for backwards compatibility. + ### XPath Deems a match if the attribute value is valid XML and matches the XPath expression supplied. An XML document will be considered to match if any elements are returned by the XPath evaluation. WireMock delegates to Java's in-built XPath engine (via XMLUnit), therefore up to (at least) Java 8 it supports XPath version 1.0. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingXPath("/todo-list[count(todo-item) = 3]")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -978,22 +1247,26 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} The above example will select elements based on their local name if used with a namespaced XML document. If you need to be able to select elements based on their namespace in addition to their name you can declare the prefix to namespace URI mappings and use them in your XPath expression: -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingXPath("/stuff:outer/more:inner[.=111]") .withXPathNamespace("stuff", "http://stuff.example.com") .withXPathNamespace("more", "http://more.example.com")) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1010,19 +1283,23 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} #### Nested value matching The XPath matcher described above can be combined with another matcher, such that the value returned from the XPath query is evaluated against it: -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingXPath("//todo-item/text()", containing("wash"))) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1038,20 +1315,24 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} If multiple nodes are returned from the XPath query, all will be evaluated and the returned match will be the one with the shortest distance. If the XPath expression returns an XML element rather than a value, this will be rendered as an XML string before it is passed to the value matcher. This can be usefully combined with the `equalToXml` matcher e.g. -Java: +{% codetabs %} +{% codetab Java %} ```java .withRequestBody(matchingXPath("//todo-item", equalToXml("Do the washing"))) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1067,21 +1348,25 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ### Absence Deems a match if the attribute specified is absent from the request. -Java: +{% codetabs %} +{% codetab Java %} ```java .withCookie("session", absent()) .withQueryParam("search_term", absent()) .withHeader("X-Absent", absent()) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1106,14 +1391,18 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ## Multipart/form-data Deems a match if a multipart value is valid and matches any or all the multipart pattern matchers supplied. As a Multipart is a 'mini' HTTP request in itself all existing Header and Body content matchers can by applied to a Multipart pattern. A Multipart pattern can be defined as matching `ANY` request multiparts or `ALL`. The default matching type is `ANY`. -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor(...) ... @@ -1124,9 +1413,9 @@ stubFor(...) .withMultipartBody(equalToJson("{}")) ) ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1150,6 +1439,9 @@ JSON: ... } ``` +{% endcodetab %} + +{% endcodetabs %} ## Basic Authentication @@ -1157,14 +1449,15 @@ Although matching on HTTP basic authentication could be supported via a correctly encoded `Authorization` header, you can also do this more simply via the API. -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor(get(urlEqualTo("/basic-auth")).withBasicAuth("user", "pass") ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1180,6 +1473,9 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} ## Dates and times @@ -1193,8 +1489,9 @@ actual dates can be truncated in various ways. You can match an incoming date/time against a fixed value e.g. "match if the X-Munged-Date request header is after x": -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor(post("/dates") .withHeader("X-Munged-Date", after("2021-05-01T00:00:00Z")) @@ -1205,9 +1502,9 @@ stubFor(post("/dates") .withHeader("X-Munged-Date", after(ZonedDateTime.parse("2021-05-01T00:00:00Z"))) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1224,22 +1521,26 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} ### Offset You can also match in incoming value against the current date/time or an offset from it: -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor(post("/dates") .withHeader("X-Munged-Date", beforeNow().expectedOffset(3, DateTimeUnit.DAYS)) .withHeader("X-Finalised-Date", before("now +2 months")) // This form and beforeNow() are equivalent .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1259,6 +1560,9 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} ### Local vs. Zoned @@ -1285,17 +1589,18 @@ HTTP RFCs 1123, 1036 and asctime (taken from C but also valid for specifying HTT It is also possible to specify your own format using [Java's date format strings](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns). -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor(post("/dates") .withHeader("X-Munged-Date", equalToDateTime("2021-06-24T00:00:00").actualFormat("dd/MM/yyyy")) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1310,6 +1615,9 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} ### Truncation @@ -1321,8 +1629,9 @@ Truncation is useful if you want to create expressions like "before the end of t It can usefully be combined with offsetting so e.g. if the match required is "after the 15th of this month" we could do as follows. -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor(post("/dates") .withRequestBody(matchingJsonPath( @@ -1331,9 +1640,9 @@ stubFor(post("/dates") ) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1351,9 +1660,15 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} Truncating the actual value can be useful when checking for equality with literal date/times e.g. to say "is in March 2020": +{% codetabs %} + +{% codetab Java %} ```java stubFor(post("/dates") .withRequestBody(matchingJsonPath( @@ -1362,9 +1677,9 @@ stubFor(post("/dates") ) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1382,6 +1697,9 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %}
The full list of available truncations is: @@ -1395,12 +1713,49 @@ The full list of available truncations is: - `first day of next year` - `last day of year` +### Order of applying offset and truncation + +By default, the date/time truncation is applied first and the offset is applied afterwards. There are scenarios, though, where the order needs to be reversed. For instance, if we want to match with the last day of the next month then the truncation should be applied last. In this case the boolean property `applyTruncationLast` should be set to true: + +{% codetabs %} + +{% codetab Java %} +```java +stubFor(get(urlPathEqualTo("/resource")) + .withQueryParam("date", equalToDateTime("now +1 months").truncateExpected(LAST_DAY_OF_MONTH).applyTruncationLast(true)) + .willReturn(ok())); +``` +{% endcodetab %} + +{% codetab JSON %} +```json +{ + "request": { + "urlPath": "/resource", + "method": "GET", + "queryParameters": { + "date": { + "equalToDateTime": "now +1 months", + "truncateExpected": "last day of month", + "applyTruncationLast": true + } + } + } +} +``` +{% endcodetab %} + +{% endcodetabs %} + +In the example above setting the `applyTruncationLast` property to true means that the expected date/time value will first be offset by one month and only afterwards truncated to the last day of that month. Which in turn means that if the current date is September 1st then the expected date will first be offset to October 1st and only then truncated to October 31st. Had the `applyTruncationLast` property been false (the default value) then the resulting expected date would be October 30th, one day off the date we were aiming for. + ## Logical AND and OR You can combine two or more matchers in an AND expression. -Java: +{% codetabs %} +{% codetab Java %} ```java // Both statements are equivalent @@ -1415,9 +1770,9 @@ stubFor(get(urlPathEqualTo("/and")) .withHeader("X-Some-Value", matching("[a-z]+").and(containing("magicvalue"))) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1438,11 +1793,15 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} Similarly you can also construct an OR expression. -Java: +{% codetabs %} +{% codetab Java %} ```java // Both statements are equivalent @@ -1457,9 +1816,9 @@ stubFor(get(urlPathEqualTo("/or")) .withQueryParam("search", matching("[a-z]+").or(absent())) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1480,6 +1839,9 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} ### Combining date matchers as JSONPath/XPath sub-matchers @@ -1489,8 +1851,9 @@ is a date/time between two points. We can do this by extracting the field using `matchesJsonPath` then matching the result of this against the `before` and `after` matchers AND'd together. -Java: +{% codetabs %} +{% codetab Java %} ```java stubFor(post("/date-range") .withRequestBody(matchingJsonPath("$.date", @@ -1498,9 +1861,9 @@ stubFor(post("/date-range") after("2020-01-01T00:00:00")))) .willReturn(ok())); ``` +{% endcodetab %} -JSON: - +{% codetab JSON %} ```json { "request": { @@ -1524,6 +1887,9 @@ JSON: } } ``` +{% endcodetab %} + +{% endcodetabs %} This would match the following JSON request body: @@ -1539,13 +1905,19 @@ You can match multiple values of a query parameter or header with below provided Exactly matcher exactly matches multiple values or patterns and make sure that it does not contain any other value. +There must be 3 values of id exactly whose values are 1, 2, and 3: + +{% codetabs %} + +{% codetab Java %} ```java -// There must be 3 values of id exactly whose values are 1, 2, and 3 stubFor(get(urlPathEqualTo("/things")) .withQueryParam("id", havingExactly("1", "2", "3")) .willReturn(ok())); ``` +{% endcodetab %} +{% codetab JSON %} ```json { "mapping": { @@ -1574,17 +1946,26 @@ stubFor(get(urlPathEqualTo("/things")) } } ``` +{% endcodetab %} + +{% endcodetabs %} + +There must be 3 values of id exactly whose values conform to the match expressions +{% codetabs %} + +{% codetab Java %} ```java -// There must be 3 values of id exactly whose values conform to the match expressions - stubFor(get(urlPathEqualTo("/things")) +stubFor(get(urlPathEqualTo("/things")) .withQueryParam("id", havingExactly( - equalTo("1"), - containing("2"), - notContaining("3") + equalTo("1"), + containing("2"), + notContaining("3") )).willReturn(ok())); ``` +{% endcodetab %} +{% codetab JSON %} ```json { "mapping": { @@ -1613,16 +1994,26 @@ stubFor(get(urlPathEqualTo("/things")) } } ``` +{% endcodetab %} + +{% endcodetabs %} Includes matcher matches multiple values or patterns specified and may contain other values as well. +The values of id must include 1, 2, and 3: + +{% codetabs %} + +{% codetab Java %} ```java -// The values of id must include 1, 2, and 3. stubFor(get(urlPathEqualTo("/things")) .withQueryParam("id", including("1", "2", "3")) .willReturn(ok())); ``` +{% endcodetab %} + +{% codetab JSON %} ```json { "mapping": { @@ -1651,17 +2042,26 @@ stubFor(get(urlPathEqualTo("/things")) } } ``` +{% endcodetab %} +{% endcodetabs %} + +Values of id must conform to the match expressions: + +{% codetabs %} + +{% codetab Java %} ```java -//values of id must conform to the match expressions stubFor(get(urlPathEqualTo("/things")) .withQueryParam("id", including( - equalTo("1"), - containing("2"), - notContaining("3") + equalTo("1"), + containing("2"), + notContaining("3") )).willReturn(ok())); ``` +{% endcodetab %} +{% codetab JSON %} ```json { "mapping": { @@ -1690,3 +2090,40 @@ stubFor(get(urlPathEqualTo("/things")) } } ``` +{% endcodetab %} + +{% endcodetabs %} + +## Logical NOT - negating matchers +You can negate any matcher using the logical NOT matcher. + +{% codetabs %} + +{% codetab Java %} +```java +stubFor( + get(urlPathEqualTo("/not")) + .withHeader("X-Some-Value", not(matching("[a-z]+"))) + .willReturn(ok())); +``` +{% endcodetab %} + +{% codetab JSON %} +```json +{ + "request": { + "urlPath": "/and", + "method": "GET", + "headers": { + "X-Some-Value": { + "not": { + "matches": "[a-z]+" + } + } + } + } +} +``` +{% endcodetab %} + +{% endcodetabs %} \ No newline at end of file diff --git a/_docs/response-templating.md b/_docs/response-templating.md index a724cda7..294ddc0c 100644 --- a/_docs/response-templating.md +++ b/_docs/response-templating.md @@ -30,6 +30,24 @@ WireMockServer wm = See [the command line docs](../standalone/java-jar/#command-line-options) for the standalone equivalents of these parameters. +Response templating can also be disabled on a per-stub basis when using the `bodyFileName` element by adding the +`disableBodyFileTemplating` parameter to the `transformerParameters` object in the stub response definition. + +```json +{ + "request": { + "method": "GET", + "urlPath": "/test" + }, + "response": { + "status": 200, + "bodyFileName": "response.json", + "transformerParameters": { + "disableBodyFileTemplating": true + } + } +} +``` ## Customising and extending the template engine @@ -163,6 +181,8 @@ wm.stubFor(get(urlPathMatching("/static/.*")) The model of the request is supplied to the header and body templates. The following request attributes are available: +`request.id` - The unique ID of each request (introduced in WireMock version `3.7.0`) + `request.url` - URL path and query `request.path` - URL path. This can be referenced in full or it can be treated as an array of path segments (like below) e.g. `request.path.3`. @@ -196,6 +216,18 @@ When the path template URL match type has been used you can additionally referen `request.body` - Request body text (avoid for non-text bodies) +`request.bodyAsBase64` - As of WireMock `3.8.0`, the Base64 representation of the request body. + +`request.multipart` - As of WireMock `3.8.0`, if the request is a multipart request (boolean). + +`request.parts` - As of WireMock `3.8.0`, the individual parts of a multipart request are exposed via the template +model. Each part can be referenced by its name and exposes a number of properties in the template model. For example, +a multipart request with a name of `text` has the following properties available: +* `request.parts.text.binary` - if the part is a binary type. +* `request.parts.text.headers.` - first value of a part header - `request.parts.text.headers.content-type` +* `request.parts.text.body` - part body as text. +* `request.parts.text.bodyAsBase64` - part body as base64. + ### Values that can be one or many A number of HTTP elements (query parameters, form fields, headers) can be single or multiple valued. The template request model and built-in helpers attempt to make @@ -323,6 +355,31 @@ Variable assignment and number helpers are available: {% endraw %} +## Val helper + +Released in WireMock version `3.6.0`, the `val` helper can be used to access values or provide a default if the value +is not present. It can also be used to assign a value to a variable much like the `assign` helper. The main difference +between `val` and `assign` is that `val` will maintain the type of the date being assigned whereas `assign` will always +assign a string. + +{% raw %} + +```handlebars +{{val request.query.search or='default'}} // the value of request.query.search or 'default' if it's not present +{{val request.query.search default='default'}} // the value of request.query.search or 'default' if it's not present +{{val request.query.search}} // the value of request.query.search or null if it's not present +{{val request.query.search or='default' assign='myVar'}} // assign the value of request.query.search or 'default' to myVar +{{val request.query.search assign='myVar'}} // assign the value of request.query.search to myVar + + +{{val (array 1 2 3) default='123'}} // [1, 2, 3] +{{val 'value for myVar' assign='myVar'}}{{myVar}} // value for myVar +{{val null or='other value for myVar' assign='myVar'}}{{myVar}} // other value for myVar +{{val 10 assign='myVar'}}{{#lt myVar 20}}Less Than{{else}}More Than{{/lt}} // Less Than +``` + +{% endraw %} + ## XPath helpers Additionally some helpers are available for working with JSON and XML. @@ -404,9 +461,66 @@ A common use case for returned node objects is to iterate over the collection wi {% endraw %} +## Format XML helper + +Introduced in WireMock version `3.10.0`, the `formatXml` helper will rewrite the input XML into a format of your choice. + +{% raw %} + +```handlebars +{{#formatXml}} +wh +{{/formatXml}} +``` + +{% endraw %} + +By default, the input will be rewritten to a "pretty" format (new lines and indentation): + +{% raw %} + +```xml + + wh + +``` + +{% endraw %} + +The format can be controlled by supplying a `format` option: + +{% raw %} + +```handlebars +{{#formatXml format='compact'}} +wh +{{/formatXml}} +``` + +{% endraw %} + +The available `format` options are `compact` (all whitespace removed) and `pretty`. + +The input XML can alternatively be supplied inline, or as a variable: + +{% raw %} + +```handlebars +{{formatXml ' wh '}} + +{{#assign 'someXml'}} wh {{/assign}} +{{formatXml someXml format='compact'}} +``` + +{% endraw %} + ## JSONPath helper -It is similarly possible to extract JSON values or sub documents via JSONPath using the `jsonPath` helper. Given the JSON +Like the `xPath` helper, it is similarly possible to extract JSON values or sub documents via JSONPath using the `jsonPath` helper. Given the JSON ```json { @@ -489,6 +603,598 @@ Without assigning to a variable: {% endraw %} +## Write as JSON helper + +Introduced in WireMock version `3.10.0`, the `toJson` helper will convert any object into a JSON string. + +{% raw %} + +```handlebars +{{toJson (array 1 2 3)}} +``` + +{% endraw %} + +emits + +{% raw %} + +```json +[ 1, 2, 3 ] +``` + +{% endraw %} + +Given a request with the following headers: + +{% raw %} + +``` +Authorization: whatever +Content-Type: text/plain +``` + +{% endraw %} + +{% raw %} + +```handlebars +{{toJson request.headers}} +``` + +{% endraw %} + +will produce + +{% raw %} + +```json +{ + "Authorization" : "whatever", + "Content-Type" : "text/plain" +} +``` + +{% endraw %} + +## Format JSON helper + +As of WireMock version `3.10.0`, the `formatJson` helper will rewrite the input JSON into a format of your choice. + +{% raw %} + +```handlebars +{{#formatJson}}{"foo":true,"bar":{"baz":false}}{{/formatJson}} +``` + +{% endraw %} + +By default, the input will be rewritten to a "pretty" format (new lines and indentation): + +{% raw %} + +```json +{ + "foo" : true, + "bar" : { + "baz" : false + } +} +``` + +{% endraw %} + +The format can be controlled by supplying a `format` option: + +{% raw %} + +```handlebars +{{#formatJson format='compact'}} +{ + "foo" : true, + "bar" : { + "baz" : false + } +} +{{/formatJson}} +``` + +{% endraw %} + +The available `format` options are `compact` (all whitespace removed) and `pretty`. + +The input JSON can alternatively be supplied inline, or as a variable: + +{% raw %} + +```handlebars +{{formatJson '{"foo":true,"bar":{"baz":false}}'}} + +{{#assign 'someJson'}} { "foo": true, "bar": { "baz": false } } {{/assign}} +{{formatJson someJson format='compact'}} +``` + +{% endraw %} + +## Adding to a JSON Array + +Introduced in WireMock version `3.10.0`, the `jsonArrayAdd` helper allows you to append an element to an existing json array. + +Its simplest form just takes two parameters, the JSON array to append to and the JSON item to be added: + +{% raw %} + +```handlebars +{{#assign 'existingArray'}} +[ + { + "id": 123, + "name": "alice" + } +] +{{/assign}} + +{{#assign 'newItem'}} +{ + "id": 321, + "name": "sam" +} +{{/assign}} + +{{jsonArrayAdd existingArray newItem}} +``` + +{% endraw %} + +The above template will produce the following JSON: + +{% raw %} + +```json +[ + { + "id": 123, + "name": "alice" + }, + { + "id": 321, + "name": "sam" + } +] +``` + +{% endraw %} + +You can also use it in block form to parse the contents of the block as the new item to add: + +{% raw %} + +```handlebars +{{#jsonArrayAdd existingArray}} +{ + "id": 321, + "name": "sam" +} +{{/jsonArrayAdd}} +``` + +{% endraw %} + +It may be convenient to default the array to an empty array if it does not exist: + +{% raw %} + +```handlebars +{{#jsonArrayAdd (val existingArray or='[]')}} +{ + "id": 321, + "name": "sam" +} +{{/jsonArrayAdd}} +``` + +{% endraw %} + + +The number of items in the array can be limited by using the `maxItems` parameter: + +{% raw %} + +```handlebars +{{#assign 'existingArray'}} +[ + { + "id": 123, + "name": "alice" + }, + { + "id": 321, + "name": "sam" + } +] +{{/assign}} + +{{#jsonArrayAdd existingArray maxItems=2}} +{ + "id": 456, + "name": "bob" +} +{{/jsonArrayAdd}} +``` + +{% endraw %} + +The above template will produce the following JSON. The first item in the array has been removed to maintain the +number of items in the array as specified by the `maxItems` parameter: + +{% raw %} + +```json +[ + { + "id": 321, + "name": "sam" + }, + { + "id": 456, + "name": "bob" + } +] +``` + +{% endraw %} + + +You can add arrays to the existing json array using this helper: + +{% raw %} + +```handlebars +{{#assign 'existingArray'}} +[ + { + "id": 123, + "name": "alice" + }, + { + "id": 321, + "name": "sam" + } +] +{{/assign}} + +{{#jsonArrayAdd existingArray}} +[ + { + "id": 456, + "name": "bob" + } +] +{{/jsonArrayAdd}} +``` + +{% endraw %} + +The above template will produce the following JSON: + +{% raw %} + +```json +[ + { + "id": 123, + "name": "alice" + }, + { + "id": 321, + "name": "sam" + }, + [ + { + "id": 456, + "name": "bob" + } + ] +] +``` + +{% endraw %} + +If you want the end result to be a single json array, you can use the `flatten` attribute: + + +{% raw %} + +```handlebars +{{#assign 'existingArray'}} +[ + { + "id": 123, + "name": "alice" + }, + { + "id": 321, + "name": "sam" + } +] +{{/assign}} + +{{#jsonArrayAdd existingArray flatten=true}} +[ + { + "id": 456, + "name": "bob" + } +] +{{/jsonArrayAdd}} +``` + +{% endraw %} + +The above template will produce the following JSON: + +{% raw %} + +```json +[ + { + "id": 123, + "name": "alice" + }, + { + "id": 321, + "name": "sam" + }, + { + "id": 456, + "name": "bob" + } +] +``` + +{% endraw %} + +You can use the `jsonArrayAdd` helper to add items to a nested array. This is achieved using the `jsonPath` property +and referencing the array you want to add an item to: + +{% raw %} + +```handlebars +{{#assign 'existingArray'}} +[ + { + "id": 123, + "names":["alice", "sam"] + }, + { + "id": 321, + "names":["fred", "neil"] + } +] +{{/assign}} + +{{#assign 'itemToAdd'}}"bob"{{/assign}} + +{{jsonArrayAdd existingArray itemToAdd jsonPath='$[0].names'}} +``` + +{% endraw %} + +The above template will produce the following JSON: + +{% raw %} + +```json +[ + { + "id": 123, + "names": [ "alice", "sam", "bob" ] + }, + { + "id": 321, + "names": [ "fred", "neil" ] + } +] +``` + +{% endraw %} + +## Merging JSON objects + +Introduced in WireMock version `3.10.0`, the `jsonMerge` helper allows you to merge two json objects. +Merging will recurse into any common keys where the values are both objects, but not into any array values, +where the value in the second object will overwrite that in the first. + +Given these two objects: + +{% raw %} + +```handlebars +{{#assign 'object1'}} +{ + "id": 456, + "forename": "Robert", + "surname": "Smith", + "address": { + "number": "12" + }, + "hobbies": [ "chess", "football" ] +} +{{/assign}} +{{#assign 'object2'}} +{ + "forename": "Robert", + "nickname": "Bob", + "address": { + "street": "High Street" + }, + "hobbies": [ "rugby" ] +} +{{/assign}} +``` + +{% endraw %} + +{% raw %} + +```handlebars +{{jsonMerge object1 object2}} +``` + +{% endraw %} + +will return this object: + +{% raw %} + +```json +{ + "id": 456, + "forename": "Robert", + "surname": "Smith", + "nickname": "Bob", + "address": { + "number": "12", + "street": "High Street" + }, + "hobbies": [ "rugby" ] +} +``` + +{% endraw %} + +Like the `jsonArrayAdd` helper, the second object can be provided as a block: + +{% raw %} + +```handlebars +{{#jsonMerge object1}} +{ + "forename": "Robert", + "nickname": "Bob", + "address": { + "street": "High Street" + }, + "hobbies": [ "rugby" ] +} +{{/jsonMerge}} +``` + +{% endraw %} + +### Removing attributes +Starting with WireMock version `3.12.0`, the `jsonMerge` helper has an optional `removeNulls` parameter which, when +set to true will remove any attributes from the resulting JSON that have null values in the second JSON document. + +So for instance, given the following template: + +{% raw %} + +```handlebars +{{#assign 'object1'}} +{ + "keepMe": 1, + "removeMe": 2 +} +{{/assign}} + +{{#jsonMerge object1 removeNulls=true}} +{ + "removeMe": null +} +{{/jsonMerge}} +``` + +{% endraw %} + +The resulting JSON would be: + +{% raw %} + +```json +{ + "keepMe": 1 +} +``` + +{% endraw %} + +## Removing from a JSON Array or Object + +The `jsonRemove` helper was introduced in WireMock `3.10.0` and allows you to remove an element from an existing json +array, or remove a key from an existing json object, by identifying it using a [json path](https://datatracker.ietf.org/doc/rfc9535/) expression. + +For instance, given an existing array like this: + +{% raw %} + +```handlebars +{{#assign 'existingArray'}} +[ + { "id": 456, "name": "bob"}, + { "id": 123, "name": "alice"}, + { "id": 321, "name": "sam"} +] +{{/assign}} +``` + +{% endraw %} + +application of this helper, which selects the object with id `123`: + +{% raw %} + +```handlebars +{{jsonRemove existingArray '$.[?(@.id == 123)]'}} +``` + +{% endraw %} + +will return this array: + +{% raw %} + +```json +[ + { "id": 456, "name": "bob"}, + { "id": 321, "name": "sam"} +] +``` + +{% endraw %} + +Given an object like this: + +{% raw %} + +```handlebars +{{#assign 'existingObject'}} + { "id": 456, "name": "bob"} +{{/assign}} +``` + +{% endraw %} + +application of this helper, which selects the key name: + +{% raw %} + +```handlebars +{{jsonRemove existingObject '$.name'}} +``` + +{% endraw %} + +will return this object: + +{% raw %} + +```json +{ "id": 456 } +``` + +{% endraw %} + ## Date and time helpers A helper is present to render the current date/time, with the ability to specify the format ([via Java's SimpleDateFormat](https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html)) and offset. @@ -585,7 +1291,7 @@ A value can be randomly selected from a literal list: {% raw %} ```handlebars -{{{pickRandom '1' '2' '3'}}} +{{pickRandom '1' '2' '3'}} ``` {% endraw %} @@ -595,7 +1301,30 @@ Or from a list passed as a parameter: {% raw %} ```handlebars -{{{pickRandom (jsonPath request.body '$.names')}}} +{{pickRandom (jsonPath request.body '$.names')}} +``` + +{% endraw %} + +If you desire multiple unique elements to be randomly pulled from the list, a `count` option can be supplied to the +helper. +In this case, the result will be a list, instead of a single value. +For example, the following template: + +{% raw %} + +```handlebars +{{pickRandom 1 2 3 4 5 count=3}} +``` + +{% endraw %} + +will produce a list similar to the following: + +{% raw %} + +``` +[3, 5, 2] ``` {% endraw %} @@ -628,9 +1357,116 @@ Likewise decimals can be produced with or without bounds: {{randomDecimal upper=12.5}} {{randomDecimal lower=-24.01}} ``` +{% endraw %} + +## Formatting numbers + +The `numberFormat` helper allows you to specify how numbers are printed. It supports +a number of predefined formats, custom format strings and various other options +including rounding mode, decimal places and locale. + +### Predefined formats +`numberFormat` supports the following predefined formats: + +* `integer` +* `currency` +* `percent` + +Predefined formats can be affected by locale, so it's usually a good idea to explicitly +specify this. + +For example, to format a decimal number as currency, specifically British pounds: + +{% raw %} +```handlebars +{{{numberFormat 123.4567 'currency' 'en_GB'}}} +``` +{% endraw %} + +Output: `£123.46`. + +Alternatively, if we wanted to output the number as a percentage: + +{% raw %} +```handlebars +{{{numberFormat 123.4567 'percent' 'en_GB'}}} +``` +{% endraw %} + +Output: `12,346%`. + +### Custom format string +For maximum control over the number format you can specify a format string: + +{% raw %} +```handlebars +{{{numberFormat 123.4567 '###.000000' 'en_GB'}}} +``` +{% endraw %} + +Output: `123.456700`. + +See the [Java DecimalFormat documentation](https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html) +for details on how to use format strings. + + +### Configuring number of digits +Separate from the format parameter, the number of digits before and after the +decimal place can be bounded using one or more of four parameters: +`maximumFractionDigits`, `minimumFractionDigits`, `maximumIntegerDigits`, `minimumIntegerDigits`. + +{% raw %} +```handlebars +{{{numberFormat 1234.567 maximumIntegerDigits=3 minimumFractionDigits=6}}} +``` +{% endraw %} + +Output: `234.567000`. + + +### Disabling grouping +By default `numberFormat` will insert commas, periods etc. per the locale between +groups of digits e.g. `1,234.5`. +This behaviour can be disabled with `groupingUsed`. + +{% raw %} +```handlebars +{{{numberFormat 12345.678 groupingUsed=false}}} +``` {% endraw %} +Output: `12345.678`. + + +### Rounding mode +The `roundingMode` parameter affects how numbers will be rounded up or down when +it's necessary to do so. + +For instance, to always round down: + +{% raw %} +```handlebars +{{{numberFormat 1.239 roundingMode='down' maximumFractionDigits=2}}} +``` +{% endraw %} + +Output: `1.23`. + +Available rounding modes are: + +* `up` +* `down` +* `half_up` +* `half_down` +* `half_even` +* `ceiling` +* `floor`. + +See the [Java RoundingMode documentation](https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html) +for the exact meaning of each of these. + + ## Fake data helpers This helper produces random fake data of the desired types available in the [Data Faker library](https://github.com/datafaker-net/datafaker). Due to the size of this library, this helper has been provided via [`RandomExtension`](https://github.com/wiremock/wiremock-faker-extension). @@ -701,6 +1537,92 @@ Providing no parameters will result in an empty array. {% endraw %} +## Array add & remove helpers +As of WireMock version `3.6.0`, the `arrayAdd` and `arrayRemove` helpers can be used to add or remove elements from an +array based on a position value or the `start` or `end` keywords. If no position is specified, the element will be +added or removed from the end of the array. + +{% raw %} + +```handlebars +{{arrayAdd (array 1 'three') 2 position=1}} // [1, 2, three] +{{arrayAdd (array 1 'three') 2 position='start'}} // [2, 1, three] +{{arrayAdd (array 1 'three') 2 position='end'}} // [1, three, 2] +{{arrayAdd (array 1 'three') 2}} // [1, three, 2] + +{{arrayRemove (array 1 2 'three') position=1}} // [1, three] +{{arrayRemove (array 1 2 'three') position='start'}} // [2, three] +{{arrayRemove (array 1 2 'three') position='end'}} // [1, 2] +{{arrayRemove (array 1 2 'three')}} // [1, 2] +``` + +{% endraw %} + +## arrayJoin helper + +Released in WireMock version `3.6.0`, the `arrayJoin` helper will concatenate the values passed to it with the +separator specified: + +{% raw %} + +```handlebars +{{arrayJoin ',' (array 'One' 'Two' 'Three')}} // One,Two,Three +{{arrayJoin ' - ' 'a' 'b' 'c'}} // a - b - c +{{arrayJoin ', ' (range 1 5)}} // 1, 2, 3, 4, 5 +{{arrayJoin (pickRandom ':') (array 'One' 'Two' 'Three')}} // One:Two:Three +{{arrayJoin '' (array 'W' 'i' 'r' 'e' 'M' 'o' 'c' 'k' ' ' 'R' 'o' 'c' 'k' 's')}} // WireMock Rocks +``` + +{% endraw %} + +You can also specify a `prefix` and `suffix` to be added to the start and end of the result: + +{% raw %} + +```handlebars +{{arrayJoin ',' (array 'One' 'Two' 'Three') prefix='[' suffix=']'}} // [One,Two,Three] +{{arrayJoin ' * ' (array 1 2 3) prefix='(' suffix=')'}} // (1 * 2 * 3) +``` + +{% endraw %} + +The `arrayJoin` helper can also be used as a block helper: + +{% raw %} + +```handlebars +{{#parseJson 'myThings'}} +[ + { "id": 1, "name": "One" }, + { "id": 2, "name": "Two" }, + { "id": 3, "name": "Three" } +] +{{/parseJson}} +[{{#arrayJoin ',' myThings as |item|}} +{ +"name{{item.id}}": "{{item.name}}" +} +{{/arrayJoin}}] // [{ "name1": "One" }, { "name2": "Two" }, { "name3": "Three" }] + + +// or the same example with the prefix and suffix parameters +{{#parseJson 'myThings'}} + [ + { "id": 1, "name": "One" }, + { "id": 2, "name": "Two" }, + { "id": 3, "name": "Three" } + ] +{{/parseJson}} +{{#arrayJoin ',' myThings prefix='[' suffix=']' as |item|}} + { + "name{{item.id}}": "{{item.name}}" + } +{{/arrayJoin}} // [{ "name1": "One" }, { "name2": "Two" }, { "name3": "Three" }] +``` + +{% endraw %} + + ## Contains helper The `contains` helper returns a boolean value indicating whether the string or array passed as the first parameter @@ -909,12 +1831,25 @@ Environment variables and system properties can be printed: {% raw %} ```handlebars +{{systemValue key='PATH'}} {{systemValue type='ENVIRONMENT' key='PATH'}} {{systemValue type='PROPERTY' key='os.path'}} ``` {% endraw %} +Since 3.5 a default value can be supplied: + +{% raw %} + +```handlebars +{{systemValue key='PATH' default='DEFAULT'}} +{{systemValue type='ENVIRONMENT' key='PATH' default='DEFAULT'}} +{{systemValue type='PROPERTY' key='os.path' default='DEFAULT'}} +``` + +{% endraw %} + If you want to add permitted extensions to your rule, then you can use the `ResponseTemplateTransformer` when constructing the response template extension. diff --git a/_docs/simulating-faults.md b/_docs/simulating-faults.md index fd2af476..77ae12ec 100644 --- a/_docs/simulating-faults.md +++ b/_docs/simulating-faults.md @@ -7,6 +7,8 @@ redirect_from: "/simulating-faults.html" description: One of the main reasons it’s beneficial to use web service fakes when testing is to inject faulty behaviour that might be difficult to get the real service to produce on demand. --- +
To go beyond simulating faults and test product reliability in unexpected fault scenarios using Chaos Engineering, learn about WireMock Cloud.
+ **One of the main reasons it's beneficial to use web service fakes when testing is to inject faulty behaviour that might be difficult to get the real service to produce on demand. In addition to being able to send @@ -108,17 +110,23 @@ You can set a random delay globally with ### Lognormal delay A lognormal distribution is a pretty good approximation of long tailed -latencies centered on the 50th percentile. It takes two parameters: +latencies centered on the 50th percentile. It takes two mandatory parameters +plus an optional third: - median - The 50th percentile of latencies. -- sigma - Standard deviation. The larger the value, the longer - the tail. +- sigma - Standard deviation of the underlying normal distribution. The larger the + value, the longer the tail. +- maxValue - (Optional) The maximum value to return. If this value is specified, it + must greater than or equal to the median otherwise an `IllegalArgumentException` will + be thrown. If a value greater than this value is generated, it will be resampled. + This is useful for shortening potential long tails that might otherwise cause timeouts + in calling clients. This option is only available from WireMock version `3.13.0` [Try different values](https://www.wolframalpha.com/input/?i=lognormaldistribution%28log%2890%29%2C+0.4%29) to find a good approximation. -To use, instantiate a `new LogNormal(median, sigma)`, or via JSON: +To use, instantiate a `new LogNormal(median, sigma)` or `new LogNormal(median, sigma, maxValue)`, or via JSON: ```json "delayDistribution": { @@ -128,6 +136,18 @@ To use, instantiate a `new LogNormal(median, sigma)`, or via JSON: } ``` +or with a maximum value: + +```json +"delayDistribution": { + "type": "lognormal", + "median": 80, + "sigma": 0.4, + "maxValue": 100 +} +``` + + ### Uniform delay A uniform distribution can be used for simulating a stable latency with diff --git a/_docs/solutions/android.md b/_docs/solutions/android.md index 580b287d..488f93d4 100644 --- a/_docs/solutions/android.md +++ b/_docs/solutions/android.md @@ -8,6 +8,9 @@ redirect_from: "/docs/android.html" logo: /images/logos/technology/android.svg --- +
Centralize and scale your API mocks with WireMock Cloud.
+ + ## Guide by Sam Edwards As documented by Sam Edwards in 2016, diff --git a/_docs/solutions/c_cpp.md b/_docs/solutions/c_cpp.md index 832036f9..d3e1460a 100644 --- a/_docs/solutions/c_cpp.md +++ b/_docs/solutions/c_cpp.md @@ -7,6 +7,8 @@ logo: /images/logos/technology/c.png og_image: solutions/testcontainers/testcontainers_c_opengraph.png --- +
Centralize and scale your API mocks with WireMock Cloud.
+ ## Testcontainers for C/C++ module Testcontainers C diff --git a/_docs/solutions/dotnet.md b/_docs/solutions/dotnet.md index 81ac805e..a332c58c 100644 --- a/_docs/solutions/dotnet.md +++ b/_docs/solutions/dotnet.md @@ -6,6 +6,7 @@ description: "Additional solutions for WireMock when using .NET" logo: /images/logos/technology/dotnet.svg --- +
Centralize and scale your API mocks with WireMock Cloud.
## WireMock.Net diff --git a/_docs/solutions/golang.md b/_docs/solutions/golang.md index 5e2b8c5a..78454888 100644 --- a/_docs/solutions/golang.md +++ b/_docs/solutions/golang.md @@ -10,6 +10,8 @@ redirect_from: hide-disclaimer: true --- +
Centralize and scale your API mocks with WireMock Cloud.
+ ## Testcontainers module for Go The WireMock community provides a [Testcontainers for Go module](https://github.com/wiremock/wiremock-testcontainers-go) module diff --git a/_docs/solutions/graphql.md b/_docs/solutions/graphql.md index 24b0f91e..49698b7f 100644 --- a/_docs/solutions/graphql.md +++ b/_docs/solutions/graphql.md @@ -11,6 +11,8 @@ redirect_from: hide-disclaimer: true --- +
Mock your GraphQL endpoints in WireMock Cloud with instant mock data and federated supergraph
+ ## WireMock Extension There is a [GraphQL extension for WireMock](https://github.com/wiremock/wiremock-graphql-extension) diff --git a/_docs/solutions/groovy.md b/_docs/solutions/groovy.md index 400f251c..11d3134d 100644 --- a/_docs/solutions/groovy.md +++ b/_docs/solutions/groovy.md @@ -6,6 +6,8 @@ description: "Additional solutions for WireMock when using Groovy" logo: /images/logos/technology/groovy.svg --- +
Centralize and scale your API mocks with WireMock Cloud.
+ ## DSL Bindings There is a [Groovy DSL binding library](https://github.com/tomjankes/wiremock-groovy) diff --git a/_docs/solutions/jvm.md b/_docs/solutions/jvm.md index ff6c06d9..d647848b 100644 --- a/_docs/solutions/jvm.md +++ b/_docs/solutions/jvm.md @@ -7,6 +7,8 @@ logo: /images/logos/technology/java.svg hide-disclaimer: true --- +
Centralize and scale your API mocks with WireMock Cloud.
+ WireMock was originally created for Java development, and there are plenty of solutions when developing applications powered by the Java Virtual Machine. @@ -64,5 +66,6 @@ sorted by alphabet: - [Clojure](https://docs.google.com/document/d/1TQccT9Bk-o2lvRVN8_mMaGttaOnwbYFLkn0DsmwGIOA/edit#heading=h.gvb3rxc1ab9p) - [Groovy](../groovy) - [Kotlin](../kotlin) +- [Pact](../pact) - [Scala](https://docs.google.com/document/d/1TQccT9Bk-o2lvRVN8_mMaGttaOnwbYFLkn0DsmwGIOA/edit#heading=h.gvb3rxc1ab9p) - [Spring Boot](../spring-boot) diff --git a/_docs/solutions/kotlin.md b/_docs/solutions/kotlin.md index 322b90d8..1fdb38bb 100644 --- a/_docs/solutions/kotlin.md +++ b/_docs/solutions/kotlin.md @@ -6,6 +6,7 @@ description: Additional solutions for WireMock when using Kotlin logo: /images/logos/technology/kotlin.svg --- +
Centralize and scale your API mocks with WireMock Cloud.
## Kotlin DSL Bindings diff --git a/_docs/solutions/kubernetes.md b/_docs/solutions/kubernetes.md index 5e22728b..47bb695a 100644 --- a/_docs/solutions/kubernetes.md +++ b/_docs/solutions/kubernetes.md @@ -7,6 +7,8 @@ logo: /images/logos/technology/kubernetes.svg hide-disclaimer: true --- +
Centralize and scale your API mocks with WireMock Cloud.
+ ## WireMock Helm Chart (Experimental) There is an [experimental Helm Chart](https://wiremock.github.io/helm-charts/) for WireMock. diff --git a/_docs/solutions/nodejs.md b/_docs/solutions/nodejs.md index 47f9f867..1d8a6c9b 100644 --- a/_docs/solutions/nodejs.md +++ b/_docs/solutions/nodejs.md @@ -6,6 +6,8 @@ description: Additional solutions for WireMock when using Node.js logo: /images/logos/technology/nodejs.svg --- +
Centralize and scale your API mocks with WireMock Cloud.
+ ## WireMock Captain WireMock Captain provides an easy interface for testing HTTP-based APIs. @@ -61,3 +63,11 @@ console.log(stubMappings); await wireMock.global.shutdown(); ``` + +## WireMock NPM package + +The WireMock NPM package is the WireMock standalone JAR packaged inside an NPM package. It has the exact same features as WireMock standalone and uses the same versioning. + +The main benefit of packaging it inside an NPM package is that the user will only need access to an NPM registry to use it. This is often the situation when working behind firewalls in organizations. + +- [GitHUb Repository](https://github.com/wiremock/wiremock-npm) diff --git a/_docs/solutions/pact.md b/_docs/solutions/pact.md new file mode 100644 index 00000000..9ef7935d --- /dev/null +++ b/_docs/solutions/pact.md @@ -0,0 +1,176 @@ +--- +layout: solution +title: "Using WireMock with Pact" +meta_title: Running WireMock with Pact | WireMock +description: The recorded invocations on WireMock can be used to generate the JSON files that are needed to publish contracts to the Pactflow broker. +redirect_from: +- "/docs/pact.html" +--- + +## WireMock Pact + +WireMock Pact will get the requests from [WireMock](https://github.com/wiremock/wiremock/) and create [Pact JSON](https://docs.pact.io/) files on the filesystem. The Pact JSON can be published to a [Pactflow broker](https://test.pactflow.io/). + +WireMock Pact contains: + +- `wiremock-pact-lib` - *A library that can transform WireMock [ServeEvent](https://github.com/wiremock/wiremock/blob/master/src/main/java/com/github/tomakehurst/wiremock/stubbing/ServeEvent.java):s to Pact JSON.* +- `wiremock-pact-extension-junit5` - *A WireMock extension, and JUnit 5, that is intended to ease usage of the library.* +- `wiremock-pact-example-springboot-app` - *A SpringBoot application that shows how it can be used.* + +WireMock Pact is released to [Maven Central](https://central.sonatype.com/search?q=se.bjurr.wiremockpact). And [available on GitHub](https://github.com/wiremock/wiremock-pact). + +## Usage - Junit 5 + +The extension is both a WireMock extension and a JUnit 5 extension. When using [`wiremock-spring-boot`](https://wiremock.org/docs/solutions/spring-boot/) it can be configured like this in a base class of your tests: + +```java +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.maciejwalkowiak.wiremock.spring.ConfigureWireMock; +import com.maciejwalkowiak.wiremock.spring.EnableWireMock; +import com.maciejwalkowiak.wiremock.spring.WireMockConfigurationCustomizer; +import org.junit.jupiter.api.extension.RegisterExtension; +import se.bjurr.wiremockpact.wiremockpactextensionjunit5.WireMockPactExtension; +import se.bjurr.wiremockpact.wiremockpactlib.api.WireMockPactConfig; + +@EnableWireMock({ + @ConfigureWireMock( + name = "wiremock-service-name", + property = "wiremock.server.url", + stubLocation = "wiremock", + configurationCustomizers = {WireMockPactBaseTest.class}) +}) +public class WireMockPactBaseTest implements WireMockConfigurationCustomizer { + @RegisterExtension + static WireMockPactExtension WIREMOCK_PACT_EXTENSION = + new WireMockPactExtension( + WireMockPactConfig.builder() // + .setConsumerDefaultValue("WireMockPactExample") // + .setProviderDefaultValue("UnknownProvider") // + .setPactJsonFolder("src/test/resources/pact-json")); + + @Override + public void customize( + final WireMockConfiguration configuration, final ConfigureWireMock options) { + configuration.extensions(WIREMOCK_PACT_EXTENSION); + } +} +``` + +### Usage - Library + +It can be used as a library. + +```java +public class ExampleTest { + private static WireMockServer server; + private static WireMockPactApi wireMockPactApi; + + @BeforeAll + public static void beforeEach() throws IOException { + server = new WireMockServer(); + server.start(); + + stubFor( + post(anyUrl()) + .willReturn( + ok() + .withHeader("content-type", "application/json") + .withBody(""" + {"a":"b"} + """)) + .withMetadata( + new Metadata( + Map.of( + WireMockPactMetadata.METADATA_ATTR, + new WireMockPactMetadata() + .setProvider("some-specific-provider"))))); + + wireMockPactApi = + WireMockPactApi.create( + new WireMockPactConfig() + .setConsumerDefaultValue("my-service") + .setProviderDefaultValue("unknown-service") + .setPactJsonFolder("the/pact-json/folder")); + wireMockPactApi.clearAllSaved(); + } + + @Test + public void testInvoke() { + // Do stuff that invokes WireMock... + } + + @AfterAll + public static void after() { + for (final ServeEvent serveEvent : server.getAllServeEvents()) { + wireMockPactApi.addServeEvent(serveEvent); + } + // Save pact-json to folder given in WireMockPactApi + wireMockPactApi.saveAll(); + server.stop(); + } +} +``` + +### Mappings metadata - Set provider in mapping + +You can adjust any mappings file like this to specify the provider of a mapping in its [metadata](https://github.com/wiremock/spec/blob/main/wiremock/wiremock-admin-api/schemas/stub-mapping.yaml) field: + +```diff +{ + "id" : "d68fb4e2-48ed-40d2-bc73-0a18f54f3ece", + "request" : { + "urlPattern" : "/animals/1", + "method" : "GET" + }, + "response" : { + "status" : 202 + }, + "uuid" : "d68fb4e2-48ed-40d2-bc73-0a18f54f3ece", ++ "metadata": { ++ "wireMockPactSettings": { ++ "provider":"some-other-system" ++ } ++ } +} +``` + +Or programmatically: + +```java + stubFor( + post(anyUrl()) + .withMetadata( + new Metadata( + Map.of( + WireMockPactMetadata.METADATA_ATTR, + new WireMockPactMetadata() + .setProvider("some-specific-provider"))))); +``` + +### Publishing to Pact broker + +Pact has a [CLI tool](https://docs.pact.io/pact_broker/publishing_and_retrieving_pacts) that can be used for publishing the contracts. But it requires Ruby or Docker. If you don't have that, perhaps `curl` is an option. There is [a shell script here](https://github.com/tomasbjerre/pactflow-publish-sh) that can also be used [via NPM](https://www.npmjs.com/package/pactflow-publish-sh). + +You may want to use something like [git-changelog-command-line](https://github.com/tomasbjerre/git-changelog-command-line) to get the next version. + +There is a test-server at https://test.pactflow.io/ that can be accessed with user `dXfltyFMgNOFZAxr8io9wJ37iUpY42M` and password `O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1`. + +```sh +current_version=$(npx git-changelog-command-line \ + --patch-version-pattern "^fix.*" \ + --print-current-version) +git_hash=`git rev-parse --short HEAD` +participant_version_number="$current_version-$git_hash" + +npx pactflow-publish-sh \ + --username=dXfltyFMgNOFZAxr8io9wJ37iUpY42M \ + --password=O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1 \ + --pactflow-broker-url=https://test.pactflow.io/contracts/publish \ + --build-url=http://whatever/ \ + --pact-json-folder=wiremock-pact-example-springboot-app/src/test/resources/pact-json \ + --participant-version-number=$participant_version_number +``` + +## Useful pages + +- [WireMock on Java and JVM](../jvm) - Most of JVM generic solutions are applicable to Spring Boot development too diff --git a/_docs/solutions/python.md b/_docs/solutions/python.md index 8f4f2f93..a57ce433 100644 --- a/_docs/solutions/python.md +++ b/_docs/solutions/python.md @@ -7,6 +7,7 @@ logo: /images/logos/technology/python.svg hide-disclaimer: true --- +
Centralize and scale your API mocks with WireMock Cloud.
## Python WireMock diff --git a/_docs/solutions/quarkus.md b/_docs/solutions/quarkus.md index eb642a3d..b4360e88 100644 --- a/_docs/solutions/quarkus.md +++ b/_docs/solutions/quarkus.md @@ -6,6 +6,7 @@ description: "Additional solutions for WireMock when developing with Quarkus" logo: /images/logos/technology/quarkus.svg --- +
Centralize and scale your API mocks with WireMock Cloud.
## WireMock Extension for Quarkus diff --git a/_docs/solutions/rust.md b/_docs/solutions/rust.md index 150db781..a2bceffc 100644 --- a/_docs/solutions/rust.md +++ b/_docs/solutions/rust.md @@ -6,6 +6,8 @@ description: "Additional solutions for WireMock when using Rust" logo: /images/logos/technology/rust.svg --- +
Centralize and scale your API mocks with WireMock Cloud.
+ ## wiremock-rs. Server implementation in Rust [LukeMathWalker/wiremock-rs](https://github.com/LukeMathWalker/wiremock-rs) is an API Mock Server implementation in Rust. diff --git a/_docs/solutions/spring-boot-integration.md b/_docs/solutions/spring-boot-integration.md new file mode 100644 index 00000000..cb32f0d5 --- /dev/null +++ b/_docs/solutions/spring-boot-integration.md @@ -0,0 +1,42 @@ +--- +layout: solution +title: "Using WireMock with Spring Boot" +meta_title: Running WireMock with Spring Boot | WireMock +description: The team behind Spring Cloud Contract have created a library to support running WireMock using the “ambient” HTTP server +redirect_from: +- "/docs/solutions/spring-boot.html" +- "/docs/solutions/spring-boot/" +logo: /images/logos/technology/spring.svg +hide-disclaimer: true +--- + +
Centralize and scale your API mocks with WireMock Cloud.
+ +## WireMock Spring Boot + +WireMock's official Spring Boot integration library is the simplest way to configure Spring Boot, Junit 5 and WireMock to work together. + + +It includes fully declarative WireMock setup, supports multiple `WireMockServer` instances, automatically sets Spring environment properties, +and does not pollute Spring application context with extra beans. + +See [WireMock Spring Boot Integration](/docs/spring-boot/) for details on installation and usage. + +You can contribute or log an issue in the [GitHub project](https://github.com/wiremock/wiremock-spring-boot). + +## Spring Cloud Contract + +WireMock provides the mocking capabilities for the Spring Cloud Contract project (a consumer-driven contract testing tool). + +See [Spring Cloud Contract WireMock](https://docs.spring.io/spring-cloud-contract/docs/current/reference/html/project-features.html#features-wiremock) for details. + +## Jetty version issues when running WireMock and Spring together. + +WireMock's main artifact is built on Jetty 11, largely so that Java 11 support can be maintained. However, many Spring applications depend on Jetty 12 and the presence of both on the classpath causes WireMock to fail with a `ClassNotFoundException` or `NoClassDefFoundError` for Servlet API classes thrown during startup. + +To rectify this, WireMock now has a dedicated Jetty 12 artifact which can be added to your project's classpath. See the [Jetty 12 page](../../jetty-12/) for details. + + +## Useful pages + +- [WireMock on Java and JVM](../jvm) - Most of JVM generic solutions are applicable to Spring Boot development too diff --git a/_docs/solutions/spring-boot.md b/_docs/solutions/spring-boot.md deleted file mode 100644 index bb27b622..00000000 --- a/_docs/solutions/spring-boot.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -layout: solution -title: "Using WireMock with Spring Boot" -meta_title: Running WireMock with Spring Boot | WireMock -toc_rank: 116 -description: The team behind Spring Cloud Contract have created a library to support running WireMock using the “ambient” HTTP server -redirect_from: -- "/docs/spring-boot.html" -logo: /images/logos/technology/spring.svg ---- - -## WireMock Spring Boot - -[WireMock Spring Boot](https://github.com/maciejwalkowiak/wiremock-spring-boot) -simplifies testing HTTP clients in Spring Boot & Junit 5 based integration tests. -It includes fully declarative WireMock setup, -supports multiple `WireMockServer` instances, -automatically sets Spring environment properties, -and does not pollute Spring application context with extra beans. - -Example: - -```java -@SpringBootTest -@EnableWireMock({ - @ConfigureWireMock(name = "user-service", property = "user-client.url") -}) -class TodoControllerTests { - - @InjectWireMock("user-service") - private WireMockServer wiremock; - - @Autowired - private Environment env; - - @Test - void aTest() { - // returns a URL to WireMockServer instance - env.getProperty("user-client.url"); - wiremock.stubFor(stubFor(get("/todolist").willReturn(aResponse() - .withHeader("Content-Type", "application/json") - .withBody(""" - [ - { "id": 1, "userId": 1, "title": "my todo" }, - ] - """) - ))); - } -} -``` - -## Spring Cloud Contract - -The team behind Spring Cloud Contract have created a library to support running WireMock using the "ambient" HTTP server. -It also simplifies some aspects of configuration and eliminates some common issues that occur when running Spring Boot and WireMock together. - -See [Spring Cloud Contract WireMock](https://docs.spring.io/spring-cloud-contract/docs/current/reference/html/project-features.html#features-wiremock) for details. - -The article [Faking OAuth2 Single Sign-on in Spring](https://engineering.pivotal.io/post/faking_oauth_sso/) -from Pivotal's blog shows how WireMock can be used to test Spring apps that use 3rd party OAuth2 login. - -## Useful pages - -- [WireMock on Java and JVM](../solutions/jvm) - Most of JVM generic solutions are applicable to Spring Boot development too diff --git a/_docs/solutions/testcontainers.md b/_docs/solutions/testcontainers.md index c1d5f83b..0925f66d 100644 --- a/_docs/solutions/testcontainers.md +++ b/_docs/solutions/testcontainers.md @@ -12,6 +12,8 @@ redirect_from: - "/testcontainers/" --- +
Centralize and scale your API mocks with WireMock Cloud.
+ The WireMock community provides modules for [Testcontainers](https://testcontainers.com/). They allow provisioning the WireMock server as a standalone container within your tests, based on [WireMock Docker](https://github.com/wiremock/wiremock-docker). diff --git a/_docs/spring-boot.md b/_docs/spring-boot.md new file mode 100644 index 00000000..e524a460 --- /dev/null +++ b/_docs/spring-boot.md @@ -0,0 +1,237 @@ +--- +layout: docs +title: "WireMock Spring Boot Integration" +meta_title: Using WireMock's Spring Boot + JUnit 5 integration | WireMock +description: Integrating WireMock, Spring Boot and JUnit 5 via the official integration library. +--- + +WireMock's Spring Boot integration provides a simple, declarative way to configure and run one or more WireMock instances their JUnit tests. + +## Installation + +Maven: +```xml + + org.wiremock.integrations + wiremock-spring-boot + {{ site.spring_boot_integration_version }} + +``` + +Gradle: +```groovy +implementation 'org.wiremock.integrations:wiremock-spring-boot:{{ site.spring_boot_integration_version }}' +``` + +## Basic usage + +The integration is enabled by adding the `@EnableWireMock` annotation to your test class. + +```java +@SpringBootTest(classes = ExamplesTests.AppConfiguration.class) +@EnableWireMock +class ExampleTests { + + @Value("${wiremock.server.baseUrl}") + private String wireMockUrl; + + @Test + void returns_a_ping() { + stubFor(get("/ping").willReturn(ok("pong"))); + + RestClient client = RestClient.create(); + String body = client.get() + .uri(wireMockUrl + "/ping") + .retrieve() + .body(String.class); + + assertThat(body, is("pong")); + } + + @SpringBootApplication + static class AppConfiguration {} +} +``` + +### Injected properties + +The example above will start a WireMock instance with a sensible set of defaults and set the following properties in the Spring context: + +- `wiremock.server.baseUrl` - Base URL of WireMock server. +- `wiremock.server.port` - HTTP port of WireMock server. + +These property names can be changed as follows: + +```java +@EnableWireMock( + @ConfigureWireMock( + baseUrlProperties = { "customUrl", "sameCustomUrl" }, + portProperties = "customPort" +)) +class CustomPropertiesTest { + + @Value("${customUrl}") + private String customUrl; + + @Value("${sameCustomUrl}") + private String sameCustomUrl; + + @Value("${customPort}") + private String customPort; + + // ... +} +``` + + +## Declarative configuration +A number of WireMock's common configuration values can be overridden via the `@ConfigureWireMock` annotation, which is used as follows: + +```java +@EnableWireMock({ + @ConfigureWireMock( + name = "my-mock", + port = 8888) +}) +``` + +This currently supports the following config items: + +* `port`: the HTTP port number. Defaults to 0 i.e. random. +* `httpsPort`: the HTTPS port number. Defaults to 0 i.e. random. +* `name`: the WireMock instance name. It is usually a good idea to set this when running multiple WireMock instances. Defaults to `wiremock`. +* `usePortFromPredefinedPropertyIfFound`: if true, take the port number from the Spring configuration. Defaults to false. +* `portProperties`: Overrides for the HTTP port property name. +* `httpsPortProperties`: Overrides for the HTTPS port property name. +* `baseUrlProperties`: Overrides for the HTTP base URL property name. +* `httpsBaseUrlProperties`: Overrides for the HTTPS base URL property name. +* `filesUnderClasspath`: Classpath root that will be used as the WireMock instance's file source. See [Customizing the mappings directory](#customizing-the-mappings-directory) for details. +* `filesUnderDirectory`: File root that will be used as the WireMock instance's file source. See [Customizing the mappings directory](#customizing-the-mappings-directory) for details. +* `extensions`: WireMock extensions to be loaded, specified as class names. +* `extensionFactories`: WireMock extension factories to be loaded, specified as class names. +* `configurationCustomizers`: Customizer classes to be applied to the configuration object passed to the WireMock instance on construction. See [Programmatic configuration](#programmatic-configuration) for details. + + + +## Programmatic configuration +If full control over the WireMock server's configuration is needed you can supply a customizer class that can call methods directly on the +WireMock configuration object. + +```java +@EnableWireMock({ + @ConfigureWireMock( + configurationCustomizers = CustomizerTest.Customizer.class) +}) +public class CustomizerTest { + + static class Customizer implements WireMockConfigurationCustomizer { + + @Override + public void customize( + WireMockConfiguration configuration, + ConfigureWireMock options + ) { + configuration.notifier(new CustomNotifier()); + } + } +} +``` + + + +## Customizing the mappings directory +By default, each `WireMockServer` is configured to load WireMock root from: + +1. Classpath *if specified* + 1. `{specified-resource-name}/{server-name}` + 2. `{specified-resource-name}` +2. Directory + 1. `{CWD}/wiremock/{server-name}` + 2. `{CWD}/stubs/{server-name}` + 3. `{CWD}/mappings/{server-name}` + 4. `{CWD}/wiremock` + 5. `{CWD}/stubs` + 6. `{CWD}/mappings` + +This can be changed as follows: + +```java +@EnableWireMock({ + @ConfigureWireMock( + name = "fs-client", + filesUnderClasspath = "some/classpath/resource", + filesUnderDirectory = "or/a/directory") +}) +``` + +## Injecting WireMock instances into the test +Sometimes it's necessary to gain programmatic access to a running WireMock instance e.g. to configure stubs or perform verifications. + +To enable this you can inject the WireMock server into a field on the test class as follows: + +```java +@SpringBootTest(classes = InjectionTest.AppConfiguration.class) +@EnableWireMock +public class InjectionTest { + + @InjectWireMock + WireMockServer wireMock; + +} +``` + +As described in the next section you can also specify the name of the desired instance when injecting: + +```java +@SpringBootTest(classes = InjectionTest.AppConfiguration.class) +@EnableWireMock({ + @ConfigureWireMock(name = "user-service") +}) +public class InjectionTest { + + @InjectWireMock("user-service") + WireMockServer mockUserService; + + @Test + void fetch_empty_list_of_users() { + + mockUserService.stubFor(get("/users").willReturn(okJson("[]"))); + + // ... + } +} +``` + +## Running multiple instances +It's typically a good idea to run a WireMock instance per API you wish to mock, primarily to avoid clashes in the URL schemes of the two (or more) APIs. + +The Spring Boot integration supports this explictly via annotation configuration. By adding more than one configuration item, multiple instances will be +started and the associated properties added to the Spring context. + +These instances can then be injected as fields on the test class to + + +```java +@SpringBootTest(classes = WireMockSpringExtensionTest.AppConfiguration.class) +@EnableWireMock({ + @ConfigureWireMock( + name = "user-service", + baseUrlProperties = "user-service.url", + portProperties = "user-service.port"), + @ConfigureWireMock( + name = "todo-service", + baseUrlProperties = "todo-service.url", + portProperties = "todo-service.port") +}) +public class WireMockSpringExtensionTest { + + @SpringBootApplication + static class AppConfiguration {} + + @InjectWireMock("user-service") + private WireMockServer mockUserService; + + @InjectWireMock("todo-service") + private WireMockServer mockTodoService; +} +``` diff --git a/_docs/standalone.md b/_docs/standalone.md index 5c731e2b..b539afcb 100644 --- a/_docs/standalone.md +++ b/_docs/standalone.md @@ -10,6 +10,8 @@ description: > We provide the JAR file and Docker image distributions for it. --- +
WireMock Cloud offers secure, publicly hosted mock APIs with nothing to install.
+ WireMock can run as a standalone service, configured via the Java API, JSON over HTTP or JSON files. We provide the JAR file and Docker image distributions for it. diff --git a/_docs/standalone/admin-api-reference.md b/_docs/standalone/admin-api-reference.md index 8476f05e..e0ee326c 100644 --- a/_docs/standalone/admin-api-reference.md +++ b/_docs/standalone/admin-api-reference.md @@ -10,6 +10,8 @@ redirect_from: - "/docs/api/" --- +
WireMock Cloud offers secure, publicly hosted mock APIs with nothing to install.
+ The WireMock admin API is described in [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md). The spec file plus an instance of Swagger UI can be accessed from a running WireMock instance under `/__admin/docs/`, e.g. `http://localhost:8080/__admin/docs/`. Below is the full API reference: diff --git a/_docs/standalone/administration.md b/_docs/standalone/administration.md index ff573545..2197f2c1 100644 --- a/_docs/standalone/administration.md +++ b/_docs/standalone/administration.md @@ -5,12 +5,14 @@ meta_title: Administration API in WireMock Standalone | WireMock description: Provides tips on managing standalone WireMock servers --- +
WireMock Cloud offers secure, publicly hosted mock APIs with nothing to install.
+ WireMock Standalone offers the REST API for administration, troubleshooting and analysis purposes. You can find the key use-cases and the full specification below. ## Fetching all of your stub mappings (and checking WireMock is working) -A GET request to the root admin URL e.g `http://localhost:8080/__admin` +A GET request to the mappings admin URL e.g `http://localhost:8080/__admin/mappings` will return all currently registered stub mappings. This is a useful way to check whether WireMock is running on the host and port you expect. diff --git a/_docs/standalone/docker.md b/_docs/standalone/docker.md index 2ebc00b4..1d6eee86 100644 --- a/_docs/standalone/docker.md +++ b/_docs/standalone/docker.md @@ -9,6 +9,8 @@ redirect_from: - "/docs/docker/" --- +
WireMock Cloud offers secure, publicly hosted mock APIs with nothing to install.
+ From version 2.31.0 WireMock has an [official Docker image](https://hub.docker.com/r/wiremock/wiremock). ## Getting started @@ -123,6 +125,7 @@ services: image: "wiremock/wiremock:latest" container_name: my_wiremock volumes: + - ./extensions:/var/wiremock/extensions - ./__files:/home/wiremock/__files - ./mappings:/home/wiremock/mappings entrypoint: ["/docker-entrypoint.sh", "--global-response-templating", "--disable-gzip", "--verbose"] diff --git a/_docs/standalone/java-jar.md b/_docs/standalone/java-jar.md index fd949cff..6383944e 100644 --- a/_docs/standalone/java-jar.md +++ b/_docs/standalone/java-jar.md @@ -11,10 +11,12 @@ redirect_from: description: The WireMock server can be run in its own process, and configured via the Java API, JSON over HTTP or JSON files. --- +
WireMock Cloud offers secure, publicly hosted mock APIs with nothing to install.
+ The WireMock server can be run in its own process, and configured via the Java API, JSON over HTTP or JSON files. -Once you have [downloaded the standalone JAR](../../download) you can run it simply by doing this: +Once you have [downloaded the standalone JAR](../../download-and-installation/) you can run it simply by doing this: ```bash $ java -jar wiremock-standalone-{{ site.wiremock_version }}.jar @@ -24,10 +26,18 @@ $ java -jar wiremock-standalone-{{ site.wiremock_version }}.jar The following can optionally be specified on the command line: +`--admin-api-basic-auth` : Require HTTP Basic authentication for admin API calls with the supplied credentials in `username:password` format + +`--admin-api-require-https` : Require HTTPS to be used to access the admin API + `--port`: Set the HTTP port number e.g. `--port 9999`. Use `--port 0` to dynamically determine a port. `--disable-http`: Disable the HTTP listener, option available only if HTTPS is enabled. +`--disable-http2-plain`: Disable HTTP/2 over plain text (HTTP). + +`--disable-http2-tls`: Disable HTTP/2 over TLS (HTTPS). + `--https-port`: If specified, enables HTTPS on the supplied port. Note: When you specify this parameter, WireMock will still, additionally, bind to an HTTP port (8080 by default). So when running multiple WireMock servers you will also need to specify the `--port` parameter in order to avoid conflicts. @@ -86,6 +96,9 @@ option is not present, the Host header value is deducted from the proxy URL. This option is only available if the `--proxy-all` option is specified. +`--preserve-user-agent-proxy-header`: As of WireMock `3.7.0`, when in proxy mode, this option will transfer the +original `User-Agent` header from the client to the proxied service. + `--proxy-via`: When proxying requests (either by using --proxy-all or by creating stub mappings that proxy to other hosts), route via another proxy server (useful when inside a corporate network that only permits @@ -94,6 +107,10 @@ internet access via an opaque proxy). e.g. `--proxy-via webproxy.mycorp.com:8080`. Also supports proxy authentication, e.g. `--proxy-via http://username:password@webproxy.mycorp.com:8080/`. +`--supported-proxy-encodings`: The set of acceptable compression methods represented in the `accept-encoding` request header +sent by WireMock when proxying or recording expressed as a comma-separated list e.g `gzip,deflate`. This is particularly useful if you want to avoid recording e.g. brotli compresssed responses +that can't then be viewed in the request log or served with a different compression scheme on playback, which can be achieved via `--supported-proxy-encodings=identity`. + `--enable-browser-proxying`: Run as a browser proxy. See [Running as a browser proxy](../../proxying#running-as-a-browser-proxy). @@ -128,6 +145,8 @@ requests. Defaults to 10. request journal (if enabled). When this limit is reached oldest entries will be discarded. +`--max-http-client-connections`: Maximum connections for Http Client. Defaults to 1000. + `--jetty-acceptor-threads`: The number of threads Jetty uses for accepting requests. @@ -142,6 +161,10 @@ e.g. `--jetty-header-request-size 16384`, defaults to 8192K. `--jetty-header-response-size`: The Jetty buffer size for response headers, e.g. `--jetty-header-response-size 16384`, defaults to 8192K. +`--jetty-idle-timeout` : Idle timeout in milliseconds for Jetty connections + +`--jetty-stop-timeout` : Timeout in milliseconds for Jetty to stop + `--async-response-enabled`: Enable asynchronous request processing in Jetty. Recommended when using WireMock for performance testing with delays, as it allows much more efficient use of container threads and therefore higher throughput. Defaults to `false`. @@ -157,7 +180,8 @@ com.mycorp.HeaderTransformer,com.mycorp.BodyTransformer. See [Extending WireMock `--local-response-templating`: Enable rendering of response definitions using Handlebars templates for specific stub mappings. -`--max-template-cache-entries`: Set the maximum number of compiled template fragments to cache. Only has any effect when response templating is enabled. Defaults to no limit. +`--max-template-cache-entries`: Set the maximum number of compiled template fragments to cache. Only has any effect when response templating is enabled. As of WireMock `3.7.0`, this defaults to 1000 +cache entries. Before WireMock `3.7.0` the default was unlimited. `--use-chunked-encoding`: Set the policy for sending responses with `Transfer-Encoding: chunked`. Valid values are `always`, `never` and `body_file`. The last of these will cause chunked encoding to be used only when a stub defines its response body from a file. @@ -168,6 +192,16 @@ The last of these will cause chunked encoding to be used only when a stub define `--disable-banner`: Prevent WireMock logo from being printed on startup +`--disable-connection-reuse`: Disable http connection reuse. Defaults to `true` + +`--disable-extensions-scanning` : Prevent extensions from being scanned and loaded from the classpath + +`--disable-optimize-xml-factories-loading` : Whether to disable optimize XML loading factories loading or not. + +`--disable-response-templating` : Disable processing of responses with Handlebars templates + +`--disable-strict-http-headers` : Whether to disable strict HTTP header handling of Jetty or not. + `--permitted-system-keys`: Comma-separated list of regular expressions for names of permitted environment variables and system properties accessible from response templates. Only has any effect when templating is enabled. Defaults to `wiremock.*`. `--enable-stub-cors`: Enable automatic sending of cross-origin (CORS) response headers. Defaults to off. @@ -182,9 +216,15 @@ The last of these will cause chunked encoding to be used only when a stub define `--proxy-pass-through`: Flag used in browser-caching in order to enable or disable pass through unmatched requests to the target indicated by the original requests. By default, this flag is enabled and let the requests pass through. -`--filename-template`: Set filename template in handlebar format. For endpoint: `GET /pets/{id}` using the format: `{{{method}}}-{{{url}}}.json` output will be `get-pets-id.json`. Default format: `{{{method}}}-{{{path}}}-{{{id}}}.json` hence by default template filename will be: `get-pets-id-1.json`. +`--filename-template`: Set filename template in handlebar format. For endpoint: `GET /pets/{id}` using the format: `{% raw %}{{{method}}}-{{{url}}}.json{% endraw %}` output will be `get-pets-id.json`. Default format: `{% raw %}{{{method}}}-{{{path}}}-{{{id}}}.json{% endraw %}` hence by default template filename will be: `get-pets-id-1.json`. Note: introduced in [3.0.0-beta-8](https://github.com/wiremock/wiremock/releases/tag/3.0.0-beta-8). +`--timeout` : The default global timeout + +`--version` : Prints wiremock version information and exits + +`--webhook-threadpool-size`: The number of threads created for processing webhook requests. This option is available as of WireMock version `3.13.0`. Defaults to 10 + `--help`: Show command line help ## Configuring WireMock using the Java client @@ -212,7 +252,7 @@ You can create a stub mapping by posting to WireMock's HTTP API: ```bash $ curl -X POST \ --data '{ "request": { "url": "/get/this", "method": "GET" }, "response": { "status": 200, "body": "Here it is!\n" }}' \ -http://localhost:8080/__admin/mappings/new +http://localhost:8080/__admin/mappings ``` And then fetch it back: @@ -336,7 +376,24 @@ Which will load your files and mappings from the packaged JAR. Note that it is not currently possible to load from the root of the classpath. -### Shutting Down +## Securing The WireMock Admin API + +You can start WireMock with the `--admin-api-basic-auth` command line option specifying your username and password in the standard `username:password` format: + +``` +java -jar wiremock-standalone.jar --admin-api-basic-auth my-username:my-super-secret-password +``` + +Any call made to the admin API after that will need the correct `Authorization` header included or a `401` will be +returned. The correct call will have the `Authorization` header with the word `Basic` followed by the Base64 +representation of your `username:password` pair: + +``` +curl -X GET --location "http://localhost:8080/__admin/requests" \ + -H "Authorization: Basic bXktdXNlcm5hbWU6bXktc3VwZXItc2VjcmV0LXBhc3N3b3Jk" +``` + +## Shutting Down To shutdown the server, either call `WireMock.shutdownServer()` or post a request with an empty body to `http://:/__admin/shutdown`. diff --git a/_docs/stateful-behaviour.md b/_docs/stateful-behaviour.md index d4a5838a..32ef3739 100644 --- a/_docs/stateful-behaviour.md +++ b/_docs/stateful-behaviour.md @@ -6,6 +6,8 @@ redirect_from: "/stateful-behaviour.html" description: Most web services tend to have some state, which changes as you and others interact with it. --- + + **Most web services tend to have some state, which changes as you and others interact with it. So it's pretty useful to be able to simulate this when you've swapped a real service for a test double.** diff --git a/_docs/stubbing.md b/_docs/stubbing.md index eb72e0c3..e6a937e0 100644 --- a/_docs/stubbing.md +++ b/_docs/stubbing.md @@ -7,6 +7,8 @@ redirect_from: "/stubbing.html" description: A core feature of WireMock is the ability to return canned HTTP responses for requests matching criteria. These are described in detail in Request Matching. --- + + A core feature of WireMock [API mocking]({{ '/' | absolute_url }}) is the ability to return canned HTTP responses for requests matching criteria. These are described in detail in [Request Matching](../request-matching/). @@ -133,8 +135,31 @@ stubFor(put("/status-only") More DSL examples [can be found here](https://github.com/tomakehurst/wiremock/tree/master/src/test/java/ignored/Examples.java#374). HTTP methods currently supported are: -`GET, POST, PUT, DELETE, HEAD, TRACE, OPTIONS`. You can specify `ANY` if -you want the stub mapping to match on any request method. +`GET, POST, PUT, DELETE, HEAD, TRACE, OPTIONS, GET_OR_HEAD`. You can specify `ANY` if +you want the stub mapping to match on any request method. `GET_OR_HEAD` is a special +method that could be used to match incoming requests for both `GET` or `HEAD` http +method. A ```HEAD``` request will result in the same behaviour expected from a web server i.e. +the ```Content-Type``` and ```Content-Length``` headers will be emitted but no response body. +A detailed guide about various HTTP methods can be found [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). +```GET_OR_HEAD``` can be used the following way + +{% codetabs %} + +{% codetab Java %} + +```java +@Test +public void getOrHeadDemo() { + stubFor(getOrHead(urlEqualTo("/get-or-head-test")) + .willReturn(okJson("{\"key\": \"value\"}"))); + + assertThat(testClient.get("/get-or-head-test").statusCode(), is(200)); +} +``` + +{% endcodetab %} + +{% endcodetabs %} ### Setting the response status message @@ -517,15 +542,29 @@ This feature is also available with the standard JAR. To use it, define the file ## Removing stubs -Stub mappings can be deleted via the Java API as follows: +Stub mappings can be deleted via the Java API, either by passing the stub object or the stub ID: ```java -StubMapping stubMapping = stubFor(get(urlEqualTo("/delete-me")) - .willReturn(aResponse().withStatus(200))); - -// Do things with the stub +UUID stubId = UUID.randomUUID(); +StubMapping stubMapping = stubFor(get("/delete-me") + .withId(stubId) + .willReturn(ok())); removeStub(stubMapping); + +// or + +removeStub(stubId); +``` + +Where stubs have metadata set on them this can be used to remove them: + +```java +stubFor(get("/delete-me") + .withMetadata(metadata().attr("tag", "payments")) + .willReturn(ok())); + +removeStubsByMetadata(matchingJsonPath("$.tag", equalTo("payments"))); ``` They can be deleted via the HTTP API by issuing a `DELETE` to `http://:/__admin/mappings/{id}` @@ -556,6 +595,14 @@ To fetch them via the HTTP API send a `GET` to `http://:/__admin/map Optionally limit and offset parameters can be specified to constrain the set returned e.g. `GET http://localhost:8080/__admin/mappings?limit=10&offset=50` +## Unmatched stub mappings + +As of WireMock version `3.13.0`, stub mappings that haven't matched any requests in the [the journal](../verifying#querying-the-request-journal) can be retrieved in Java by calling `WireMock.findUnmatchedStubs()`. + +This can be useful when combined with [record and playback](../record-playback/) to prune unused stub mappings. + +Via the HTTP API, send a `GET` or `DELETE` request to `http://:/__admin/mappings/unmatched` to fetch or remove them, respectively. Note that a `DELETE` request will not remove any associated body files under the `__files` directory. + ## Getting a single stub mapping by ID A single stub mapping can be retrieved by ID in Java by calling `WireMock.getSingleStubMapping(id)` where `id` is the @@ -630,3 +677,20 @@ This can be changed by setting `duplicatePolicy` in the JSON to `IGNORE` or call If you want to ensure that the only stubs loaded after the import has completed are the ones it contains, you can set `"deleteAllNotInImport": true` in the JSON or call `deleteAllExistingStubsNotInImport()` on the Java builder. + +### Disabling Gzip at the ResponseDefinitionBuilder + +If you want to user Gzip disabled response option at the ResponseDefinitionBuilder level. +You can use `.withGzipDisabled(true)` + +```java + +wireMockServer.stubFor(get(urlEqualTo("/todo/items")) + .willReturn(aResponse() + .withStatus(200) + .withGzipDisabled(true) + .withBody( + "Here is some kind of response body" + + "Here is some kind of response body" + + "Here is some kind of response body"))); +``` diff --git a/_docs/v4.md b/_docs/v4.md new file mode 100644 index 00000000..43128896 --- /dev/null +++ b/_docs/v4.md @@ -0,0 +1,57 @@ +--- +layout: docs +title: WireMock v4 +meta_title: "Changes to WireMock in version 4.x" +description: > + WireMock is available as a standalone service (for Docker of Java), Java library + and integrations for modern languages and technology stacks. +redirect_from: "/v4.html" +--- + + + +## v4 Beta + +Version 4 of WireMock is currently in beta. It is under active development and we recommend you try it out. We would love +to hear your feedback over on the community slack - [https://slack.wiremock.org/](https://slack.wiremock.org/) + +We have given these releases a beta label due to the fact that as we move forwards with the `4.x` release there **will be +breaking changes**. These are the current updates to the `4.x` release: + +* Java 17 is now the baseline java version +* Jetty 12 is shipped by default so there is no longer a specific jetty 12 release of `4.x` and Jetty 11 is no longer supported + +## Breaking changes and how to migrate + +### Multiple Content-Type headers + +In v3 using Jetty 11, if you configured a stub with multiple `Content-Type` headers, Jetty 11 stripped out all but the +last. + +In v4.x WireMock will return all the `Content-Type` headers. This may break some clients which do not know what to do if +an HTTP response has multiple `Content-Type` headers. + +Solution: only configure a single `Content-Type` header on a stub. + +### Removed transitive dependencies + +v4 no longer has transitive dependencies on the following libraries: + +* org.eclipse.jetty:jetty-webapp (package org.eclipse.jetty.webapp) - Jetty 12 equivalent is org.eclipse.jetty.ee10:jetty-ee10-webapp +* org.eclipse.jetty.ee10:jetty-ee10-webapp (package org.eclipse.jetty.ee10.webapp) +* org.eclipse.jetty:jetty-alpn-client (package org.eclipse.jetty.alpn.client) +* org.eclipse.jetty:jetty-alpn-java-client (package org.eclipse.jetty.alpn.java.client) +* org.eclipse.jetty:jetty-client (package org.eclipse.jetty.client) +* org.eclipse.jetty:jetty-ee (package org.eclipse.jetty.ee) +* org.eclipse.jetty:jetty-proxy (package org.eclipse.jetty.proxy) +* org.eclipse.jetty:jetty-xml (package org.eclipse.jetty.xml) + +If your code depends on classes in these packages you will need to bring the dependencies in directly. + +The following transitive dependencies have been replaced and may require changes to package names: + +* org.eclipse.jetty:jetty-servlet -> org.eclipse.jetty.ee10:jetty-ee10-servlet +* org.eclipse.jetty:jetty-servlets -> org.eclipse.jetty.ee10:jetty-ee10-servlets +* org.eclipse.jetty.http2:http2-common -> org.eclipse.jetty.http2:jetty-http2-common +* org.eclipse.jetty.http2:http2-hpack -> org.eclipse.jetty.http2:jetty-http2-hpack +* org.eclipse.jetty.http2:http2-server -> org.eclipse.jetty.http2:jetty-http2-server diff --git a/_docs/verifying.md b/_docs/verifying.md index 4aee216b..c9abd2b3 100644 --- a/_docs/verifying.md +++ b/_docs/verifying.md @@ -7,6 +7,8 @@ redirect_from: "/verifying.html" description: Verifying and querying requests relies on the request journal, which is an in-memory log of received requests. This can be disabled for load testing. --- + + The WireMock server records all requests it receives in memory (at least until it is [reset](../stubbing#reset)). This makes it possible to verify that a request matching a specific pattern was received, and also to fetch diff --git a/_docs/webhooks-and-callbacks.md b/_docs/webhooks-and-callbacks.md index 809fcfbe..a7d2382a 100644 --- a/_docs/webhooks-and-callbacks.md +++ b/_docs/webhooks-and-callbacks.md @@ -276,6 +276,61 @@ JSON: } ``` +## Observing webhook events +As of WireMock `3.7.0`, successful webhook requests and responses are logged as Sub Events in the request log. Any +errors that happen as part of the webhook request (not able to contact the target site or error in the handlebars +template for example) are logged as error Sub Events in the request log. An example of a successful request/response +webhook Sub Event: + +```json +{ + "subEvents": [ + { + "type": "WEBHOOK_REQUEST", + "timeOffsetNanos": 0, + "data": { + "url": "/2865e463-1f98-4899-8837-90b89364a5dc", + "absoluteUrl": "https://example.com/2865e463-1f98-4899-8837-90b89364a5dc", + "method": "POST", + "headers": { + "Content-Type": "application/json", + "Accept": "application/json" + }, + "browserProxyRequest": false, + "loggedDate": 1719826613928, + "bodyAsBase64": "eyJvbGRTdGF0ZSI6IHt9LCAibmV3U3RhdGUiOiB7fX0=", + "body": "{\"oldState\": {}, \"newState\": {}}", + "scheme": "https", + "host": "example.com", + "port": 443, + "loggedDateString": "2024-07-01T09:36:53.928Z", + "queryParams": {}, + "formParams": {} + } + }, + { + "type": "WEBHOOK_RESPONSE", + "timeOffsetNanos": 0, + "data": { + "status": 200, + "headers": { + "Transfer-Encoding": "chunked", + "X-Token-Id": "2865e463-1f98-4899-8837-90b89364a5dc", + "Cache-Control": "no-cache, private", + "Server": "nginx", + "X-Request-Id": "f530c738-bc00-48f2-8382-2394c25a32c6", + "Vary": "Accept-Encoding", + "Date": "Mon, 01 Jul 2024 09:36:54 GMT", + "Content-Type": "text/html; charset=UTF-8" + }, + "bodyAsBase64": "", + "body": "" + } + } + ] +} +``` + ## Extending webhooks Webhook behaviour can be further customised in code via an extension point. diff --git a/_includes/downloads-v4.html b/_includes/downloads-v4.html new file mode 100644 index 00000000..ec91ee56 --- /dev/null +++ b/_includes/downloads-v4.html @@ -0,0 +1,131 @@ + + + +
+
+
+
+
+ Maven +
+
+ Gradle +
+
+ Kotlin +
+
+ SBT +
+ {% if include.show_standalone %} +
+ Java +
+
+ Docker +
+ {% endif %} +
+
+
+
+
    +
  • Maven
  • +
  • Gradle Groovy
  • +
  • Gradle Kotlin
  • +
  • Scala SBT
  • + {% if include.show_standalone %} +
  • Standalone
  • +
  • Docker
  • + {% endif %} +
+
+
+

Add the following to your project's pom.xml dependencies:

+{% highlight xml %} + + org.wiremock + wiremock + {{ site.wiremock_4_version }} + test + +{% endhighlight %} +

Then follow the next steps for JUnit 5+ or plain Java.

+
+
+

Add the following to your project's build.gradle:

+{% highlight groovy %} +testImplementation "org.wiremock:wiremock:{{ site.wiremock_4_version }}" +{% endhighlight %} +

Then follow the next steps for JUnit 5+ or plain Java.

+
+
+

Add the following to your project's build.gradle.kts:

+{% highlight kotlin %} +testImplementation("org.wiremock:wiremock:{{ site.wiremock_4_version }}") +{% endhighlight %} +

Then follow the next steps for JUnit 5+ or plain Java.

+
+
+

Add the following to your project’s build.sbt:

+{% highlight scala %} +libraryDependencies += + "org.wiremock" % "wiremock" % "{{ site.wiremock_version }}" % Test +{% endhighlight %} +
+
+

+ Download the latest standalone JAR + then run the following in a terminal: +

+{% highlight bash %} +java -jar wiremock-standalone-{{ site.wiremock_4_version }}.jar +{% endhighlight %} +

Learn more in the running standalone guide.

+
+
+
+
+
+
diff --git a/_includes/masthead.html b/_includes/masthead.html index e49257ec..65cb326c 100644 --- a/_includes/masthead.html +++ b/_includes/masthead.html @@ -1,15 +1,17 @@