Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Provides patterns for unit testing external REST APIs using WireMock. Stubs API responses, verifies request details, simulates failures (timeouts, 4xx/5xx errors), and validates HTTP client behavior without real network calls. Use when testing service integrations with external APIs or mocking HTTP endpoints.
.claude/skills/giuseppe-trisciuoglio-unit-test-wiremock-rest-api/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-19 | ✗→✓ | ▲ Improved | -2% | 0% |
| case-01 | ✗→✓ | ▲ Improved | -13% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 4% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 14% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 10% | 0% |
Patterns for testing external REST API integrations with WireMock: stubbing responses, verifying requests, error scenarios, and fast tests without network dependencies.
@RegisterExtension WireMockExtension with dynamicPort()wireMock.getRuntimeInfo().getHttpBaseUrl() as base URLstubFor() with request matching (URL, headers, body)verify() to ensure correct API usageIf stub not matching: Check URL encoding, header names, use urlEqualTo for query params.
If tests hanging: Configure connection timeouts in HTTP client; use withFixedDelay() for timeout simulation.
If port conflicts: Always use wireMockConfig().dynamicPort().
xml<dependency> <groupId>org.wiremock</groupId> <artifactId>wiremock</artifactId> <version>3.4.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency>
javaimport 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"))); } }
See references/advanced-examples.md for error scenarios, body verification, timeout simulation, and stateful testing.
@RegisterExtension resets WireMock between testsreferences/advanced-examples.md - Error scenarios, body verification, timeouts| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-18 | pass→pass | 9,355 | 2,659 | -72% | 1 | 1 | 0% | 1,393 | 1,300 | -7% | 0 | 0 | — |
case-19 | fail→pass | 9,218 | 3,379 | -63% | 1 | 1 | 0% | 1,434 | 1,407 | -2% | 0 | 0 | — |
case-01 | fail→pass | 16,592 | 10,892 | -34% | 1 | 1 | 0% | 3,758 | 3,272 | -13% | 0 | 0 | — |
case-02 | fail→pass | 15,137 | 15,294 | +1% | 1 | 1 | 0% | 3,076 | 3,191 | +4% | 0 | 0 | — |
case-03 | fail→pass | 14,700 | 10,453 | -29% | 1 | 1 | 0% | 2,741 | 3,130 | +14% | 0 | 0 | — |
case-04 | pass→pass | 11,979 | 11,929 | -0% | 1 | 1 | 0% | 2,766 | 3,307 | +20% | 0 | 0 | — |
case-05 | pass→pass | 15,871 | 12,594 | -21% | 1 | 1 | 0% | 3,075 | 3,805 | +24% | 0 | 0 | — |
case-06 | pass→pass | 13,163 | 10,182 | -23% | 1 | 1 | 0% | 2,679 | 3,140 | +17% | 0 | 0 | — |
case-07 | pass→pass | 7,445 | 5,624 | -24% | 1 | 1 | 0% | 1,630 | 2,050 | +26% | 0 | 0 | — |
case-08 | pass→pass | 7,209 | 4,513 | -37% | 1 | 1 | 0% | 1,266 | 1,790 | +41% | 0 | 0 | — |
case-09 | fail→pass | 13,550 | 8,445 | -38% | 1 | 1 | 0% | 2,340 | 2,564 | +10% | 0 | 0 | — |
case-10 | pass→pass | 9,489 | 3,427 | -64% | 1 | 1 | 0% | 1,604 | 1,552 | -3% | 0 | 0 | — |
case-11 | pass→pass | 5,077 | 4,138 | -18% | 1 | 1 | 0% | 818 | 1,524 | +86% | 0 | 0 | — |
case-12 | pass→pass | 5,945 | 3,194 | -46% | 1 | 1 | 0% | 1,009 | 1,546 | +53% | 0 | 0 | — |
case-13 | pass→pass | 5,278 | 4,007 | -24% | 1 | 1 | 0% | 853 | 1,761 | +106% | 0 | 0 | — |
case-14 | pass→pass | 3,246 | 1,955 | -40% | 1 | 1 | 0% | 495 | 1,271 | +157% | 0 | 0 | — |
case-15 | pass→pass | 5,418 | 3,338 | -38% | 1 | 1 | 0% | 883 | 1,601 | +81% | 0 | 0 | — |
case-16 | pass→pass | 4,971 | 3,531 | -29% | 1 | 1 | 0% | 987 | 1,606 | +63% | 0 | 0 | — |
case-17 | pass→pass | 9,066 | 7,339 | -19% | 1 | 1 | 0% | 1,497 | 1,744 | +16% | 0 | 0 | — |
case-20 | pass→pass | 12,919 | 4,463 | -65% | 1 | 1 | 0% | 1,966 | 1,652 | -16% | 0 | 0 | — |
case-21 | pass→pass | 10,838 | 5,535 | -49% | 1 | 1 | 0% | 1,689 | 1,821 | +8% | 0 | 0 | — |
case-22 | pass→pass | 7,385 | 2,653 | -64% | 1 | 1 | 0% | 1,155 | 1,317 | +14% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.