Back to Testing & Quality Assurance

unit-test-wiremock-rest-api

WireMockREST APIUnit TestingJavaMockingIntegration TestingHTTP StubbingTest Automation
282📄 MIT🕒 2026-06-15Source ↗

Install this skill

npx skills add giuseppe-trisciuoglio/developer-kit

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

This skill facilitates isolated testing of Java-based HTTP clients by substituting live remote web services with local, controlled mock servers. By using the WireMock library, you can intercept outgoing network calls within your JUnit 5 test suite to return predefined responses, such as valid JSON payloads, specific error status codes, or deliberate latency. The implementation centers on the WireMockExtension, which manages the lifecycle of a stubbed server on a dynamic port, ensuring tests remain fast and portable. This approach eliminates external dependencies, bypassing rate limits and network instability during development. It allows for rigorous verification of client-side request construction, header validation, and payload processing, ensuring your integration logic behaves predictably before moving to staging or production environments.

When to Use This Skill

  • Validating client-side parsing of complex JSON third-party responses
  • Ensuring code resilience against upstream service timeouts or service outages
  • Confirming that security tokens or headers are correctly appended to outgoing calls
  • Developing integration logic without requiring access to a sandbox or public API

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • How do I unit test a class that calls an external REST API?
  • Set up WireMock in a JUnit 5 test project
  • Verify my Java client sends the correct headers to an API
  • Simulate a 500 error for a REST API client test
  • Stub a JSON response for a specific API endpoint

Pro Tips

  • 💡Organize complex WireMock stubs into separate JSON files or helper methods for better readability and reusability, especially for multi-scenario tests.
  • 💡Leverage WireMock's advanced matching capabilities (e.g., regex for URLs, JSONPath for request bodies) to create highly specific and robust stub definitions.
  • 💡Integrate WireMock into your build process to ensure all API client logic is thoroughly tested without requiring a running backend service during development or CI/CD.

What this skill does

  • Injects stubbed HTTP responses to mock remote server behavior
  • Verifies that outgoing client requests contain correct headers and bodies
  • Simulates transient errors and HTTP status codes like 404, 500, or 403
  • Uses dynamic port allocation to prevent local environment resource conflicts
  • Integrates directly into JUnit 5 lifecycles via RegisterExtension

When not to use it

  • When testing true end-to-end connectivity across actual network infrastructure
  • For load testing live services or identifying performance bottlenecks in external APIs

Example workflow

  1. Add WireMock and JUnit 5 dependencies to your Maven or Gradle build file
  2. Declare a WireMockExtension field in your test class to manage the server lifecycle
  3. Define a stub in your test method using stubFor to specify the expected URL and return body
  4. Initialize your HTTP client with the base URL provided by the WireMock runtime info
  5. Execute the client method and perform assertions on the returned object
  6. Call verify to confirm the client sent the expected request to the wiremock server

Prerequisites

  • Basic knowledge of JUnit 5 test annotations
  • Familiarity with Java HTTP client implementation
  • Maven or Gradle project structure

Pitfalls & limitations

  • !Hardcoding port numbers instead of using dynamic port assignment causes tests to fail in parallel environments
  • !Forgetting to verify specific request attributes can lead to silent failures where the client sends incorrect data
  • !Failing to match URL parameters or headers exactly as defined in the test stub causes unexpected 404 results

FAQ

Why use WireMock instead of a Mockito mock of my HTTP client?
WireMock tests the actual HTTP serialization and network-layer logic, whereas Mockito mocks bypass the client's internal logic entirely.
Does this require a Spring context?
No, this implementation uses the native WireMock extension for JUnit 5, allowing for lightweight unit tests without loading the full Spring framework.
How do I prevent port conflicts during CI/CD builds?
Always initialize the extension using wireMockConfig().dynamicPort(), which lets the operating system assign a free port per test execution.

How it compares

While manual mocking requires complex instrumentation of network libraries, WireMock provides a declarative DSL that creates a real HTTP socket, ensuring your actual client code is executed exactly as it would be against a production server.

Source & trust

282 stars📄 MIT🕒 Updated 2026-06-15
📄 Full skill instructions — original source: giuseppe-trisciuoglio/developer-kit
# Unit Testing REST APIs with WireMock

Test interactions with third-party REST APIs without making real network calls using WireMock. This skill focuses on pure unit tests (no Spring context) that stub HTTP responses and verify requests.

## When to Use This Skill

Use this skill when:
- Testing services that call external REST APIs
- Need to stub HTTP responses for predictable test behavior
- Want to test error scenarios (timeouts, 500 errors, malformed responses)
- Need to verify request details (headers, query params, request body)
- Integrating with third-party services (payment gateways, weather APIs, etc.)
- Testing without network dependencies or rate limits
- Building unit tests that run fast in CI/CD pipelines

## Core Dependencies

### Maven
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>3.4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>


### Gradle
dependencies {
testImplementation("org.wiremock:wiremock:3.4.1")
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.assertj:assertj-core")
}


## Basic Pattern: Stubbing and Verifying

### Simple Stub with WireMock Extension

import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.assertj.core.api.Assertions.assertThat;

class ExternalWeatherServiceTest {

@RegisterExtension
static WireMockExtension wireMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();

@Test
void shouldFetchWeatherDataFromExternalApi() {
wireMock.stubFor(get(urlEqualTo("/weather?city=London"))
.withHeader("Accept", containing("application/json"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"city\":\"London\",\"temperature\":15,\"condition\":\"Cloudy\"}")));

String baseUrl = wireMock.getRuntimeInfo().getHttpBaseUrl();
WeatherApiClient client = new WeatherApiClient(baseUrl);
WeatherData weather = client.getWeather("London");

assertThat(weather.getCity()).isEqualTo("London");
assertThat(weather.getTemperature()).isEqualTo(15);

wireMock.verify(getRequestedFor(urlEqualTo("/weather?city=London"))
.withHeader("Accept", containing("application/json")));
}
}


## Testing Error Scenarios

### Test 4xx and 5xx Responses

@Test
void shouldHandleNotFoundError() {
wireMock.stubFor(get(urlEqualTo("/api/users/999"))
.willReturn(aResponse()
.withStatus(404)
.withBody("{\"error\":\"User not found\"}")));

WeatherApiClient client = new WeatherApiClient(wireMock.getRuntimeInfo().getHttpBaseUrl());

assertThatThrownBy(() -> client.getUser(999))
.isInstanceOf(UserNotFoundException.class)
.hasMessageContaining("User not found");
}

@Test
void shouldRetryOnServerError() {
wireMock.stubFor(get(urlEqualTo("/api/data"))
.willReturn(aResponse()
.withStatus(500)
.withBody("{\"error\":\"Internal server error\"}")));

ApiClient client = new ApiClient(wireMock.getRuntimeInfo().getHttpBaseUrl());

assertThatThrownBy(() -> client.fetchData())
.isInstanceOf(ServerErrorException.class);
}


## Request Verification

### Verify Request Details and Payload

@Test
void shouldVerifyRequestBody() {
wireMock.stubFor(post(urlEqualTo("/api/users"))
.willReturn(aResponse()
.withStatus(201)
.withBody("{\"id\":123,\"name\":\"Alice\"}")));

ApiClient client = new ApiClient(wireMock.getRuntimeInfo().getHttpBaseUrl());
UserResponse response = client.createUser("Alice");

assertThat(response.getId()).isEqualTo(123);

wireMock.verify(postRequestedFor(urlEqualTo("/api/users"))
.withRequestBody(matchingJsonPath("$.name", equalTo("Alice")))
.withHeader("Content-Type", containing("application/json")));
}


## Best Practices

- **Use dynamic port** to avoid port conflicts in parallel test execution
- **Verify requests** to ensure correct API usage
- **Test error scenarios** thoroughly
- **Keep stubs focused** - one concern per test
- **Reset WireMock** between tests automatically via @RegisterExtension
- **Never call real APIs** - always stub third-party endpoints

## Troubleshooting

**WireMock not intercepting requests**: Ensure your HTTP client uses the stubbed URL from wireMock.getRuntimeInfo().getHttpBaseUrl().

**Port conflicts**: Always use wireMockConfig().dynamicPort() to let WireMock choose available port.

## References

- [WireMock Official Documentation](https://wiremock.org/)
- [WireMock Stubs and Mocking](https://wiremock.org/docs/stubbing/)
- [JUnit 5 Extensions](https://junit.org/junit5/docs/current/user-guide/#extensions)

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/unit-test-wiremock-rest-api/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/giuseppe-trisciuoglio/developer-kit/unit-test-wiremock-rest-api/SKILL.md
  • Cursor: ~/.cursor/skills/giuseppe-trisciuoglio/developer-kit/unit-test-wiremock-rest-api/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/giuseppe-trisciuoglio/developer-kit/unit-test-wiremock-rest-api/SKILL.md

🚀 Install with CLI:
npx skills add giuseppe-trisciuoglio/developer-kit

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid testing & quality assurance issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Testing & Quality Assurance and is published by Giuseppe Trisciuoglio, maintained in giuseppe-trisciuoglio/developer-kit.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.