StableMock Logo

StableMock

Auto-record them APIs to WireMock stubs. Zero config.

Stop hand-writing mocks for flaky external APIs. StableMock records real HTTP calls during your tests, turns them into WireMock stubs, and replays them reliably — even when request data changes.

Built for JUnit 5. Works offline. Free & open source.

Where your tests find their rest.

Cowboy looking at logo

Why StableMock?

StableMock is a JUnit 5 extension that automatically records third-party API calls and replays them using WireMock — without manual setup, Docker, or brittle matchers.

Perfect for mocking external/third-party APIs • Fast, lightweight HTTP mocking without Docker overhead • Tests with dynamic request data that need intelligent pattern matching

🤠

Zero-Config Recording

Add @U to your test and StableMock automatically records and replays all external HTTP calls — no config files, no setup.

🪢

Smart Pattern Matching

Handles timestamps, IDs, and tokens with ignore patterns like json:timestamp or xml://MessageID. Auto-detects dynamic fields.

🌵

Native JUnit 5

Built right into JUnit 5. No manual lifecycle management needed. Parallel execution? Each test gets its own isolated WireMock instance.

🐂

GraphQL & REST Support

Handles GraphQL (auto-detected), REST, SOAP, and XML. Even nested fields like json:user.session.token work seamlessly. Supports namespaced XML too.

🌾

100% Free & Open Source

MIT licensed. No paid tiers, no cloud dependencies. Free forever. Perfect for mocking external APIs you don't control.

🐎

Offline Testing

Perfect for CI/CD pipelines. Runs tests offline with recorded mocks. No waiting on real APIs or worrying about changes. Record once, replay forever.

Feature Comparison

Compare StableMock against WireMock 3 and WireMock Cloud. See why developers choose the open-source solution that works out of the box.

Zero-config recording • Smart pattern matching • Free forever

Feature
StableMock
WireMock 3 WireMock Cloud
Setup & Recording ✅ *Zero-Config Recording* — just annotate & go ⚠️ Manual setup required ✅ SaaS recording & cloud UI
Dynamic Data Handling ✅ *Smart Patterns* that learn what changes ❌ Manual matchers needed ✅ Yes (dynamic templates, stateful mocks)
JUnit 5 Integration ✅ Native annotation, seamless lifecycle ⚠️ Requires manual lifecycle handling ✅ Native integration + enterprise CI support
Nested JSON/XML Support ✅ Intuitive DSL (e.g., json:user.session.token) ❌ Custom JSONPath/XPath required ✅ Yes (supports complex formats)
GraphQL Support ✅ Auto-detected, built-in support ❌ Manual config only ✅ Yes — full GraphQL/gRPC support
Scenarios / Sequential Responses ✅ Built-in support for flows & sequences ✅ Possible but heavy manual setup ✅ Fully managed scenario/state machine flows
Auto-Detect Dynamic Fields ✅ Detects & ignores fluctuating fields automatically ❌ No built-in detection/learning ⚠️ Offers advanced automation, but SaaS-only
Playback Reliability ✅ Stable reproducibility — record once, run anywhere ⚠️ Depends on custom mappings ⚠️ Cloud-first, offline may be limited
Offline & CI/CD Ready ✅ Local files + dedicated Gradle/CI tasks ✅ Local use supported ⚠️ Primarily cloud-based; offline workflows limited
Free & Open Source ✅ MIT License — fully free, unrestricted ✅ Apache 2.0 — open source ❌ SaaS model (free tier exists)

WireMock Cloud features listed for comparison only.

Get Started in Seconds

This test runs twice automatically:

  1. 1. First run: Records real API responses and identifies changing fields (timestamps, IDs, etc.)
  2. 2. Second run: Replays responses offline using WireMock with auto-generated ignore patterns for dynamic fields

StableMock compares the two runs to detect which fields change between executions, then automatically creates ignore patterns so your tests remain stable even when request data varies.

@U(urls = { "https://api1.com", "https://api2.com" },
   properties = { "app.api1.url", "app.api2.url" })
@SpringBootTest
class MyTest extends BaseStableMockTest {
    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        autoRegisterProperties(registry, MyTest.class);
    }
    
    @Test
    public void myTest() {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://localhost:8080/api/users"))
            .GET()
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString()
        );
        assertEquals(200, response.statusCode());
    }
}

That's it. No configuration. No setup. Just works. Even with everchanging request attributes.

Stop Rewriting Mocks By Hand, Son

Record once. Test offline forever.

Join developers who've made the switch. Free forever. No credit card required.

🌾 MIT License | 🚀 Open Source | 💯 Free Forever