← Back to posts
comparison guide self-hosted · · 12 min read

Best Self-Hosted API Mocking Tools: WireMock vs Mockoon vs MockServer 2026

Complete guide to self-hosted API mocking tools in 2026. Compare WireMock, MockServer, and Mockoon for local development, CI/CD testing, and service virtualization.

OS
Editorial Team

When you are building a frontend that depends on a backend that does not exist yet, or testing a microservice that needs responses from three other services, you run into a classic problem: how do you keep working when your dependencies are not ready? API mocking is the answer. By running a local server that pretends to be the real API, you can develop, test, and iterate without waiting on anyone else.

This guide covers the three most popular open-source, self-hosted API mocking tools in 2026 — WireMock, MockServer, and Mockoon — and shows you how to set them up, configure them, and use them in real workflows.

Why Self-Host Your API Mocks?

You could use a cloud-based mock service, but self-hosting gives you advantages that matter in production environments:

  • Zero latency: A local mock responds in milliseconds, not hundreds of milliseconds. Fast feedback loops matter when you are running thousands of tests.
  • No external dependencies: Your CI pipeline does not need internet access or third-party credentials. Everything runs inside your docker network.
  • Full data control: You decide what data the mock returns. No risk of sensitive test data leaving your infrastructure.
  • Cost: All three tools covered here are free and open-source. No per-request pricing or usage caps.
  • Offline development: Developers can work on trains, in airports, or anywhere without a connection.
  • Deterministic testing: Cloud mocks can have network hiccups. A local mock gives you consistent, repeatable test behavior every single time.

Quick Comparison at a Glance

FeatureWireMockMockServerMockoon
LanguageJavaJavaElectron/Node.js
UINo (JSON/DSL only)No (JSON/DSL only)Yes (desktop app)
Record & PlaybackYesYesYes
Request MatchingAdvanced (regex, JSONPath, XPath)Advanced (regex, JSONPath, XMLPath)Basic (URL, method, headers)
Stateful ScenariosYes (state machines)Yes (expectations + sequences)Limited
GraphQL SupportYesYesNo
gRPC SupportYes (via extensions)NoNo
Docker ImageYesYesCommunity only
JUnit/TestNG IntegrationExcellentExcellentNo
PerformanceHigh (embedded or standalone)High (standalone or embedded)Medium (desktop app)
Best ForEnterprise teams, CI/CD pipelinesComplex contract testing, multi-service mockingSolo developers, quick prototypes

WireMock: The Industry Standard

WireMock is the most widely adopted API mocking tool in the Java ecosystem, but it works for any language via its REST API. Created by Tom Akehurst and now maintained by WireMock Inc., it powers mocking at companies ranging from startups to Fortune 500 teams.

Key Features

  • Standalone server mode: Run as a JAR or Docker container and stub any HTTP/HTTPS endpoint.
  • Library mode: Embed directly in Java tests using the JUnit 4/5 extensions.
  • Response templiving: Use Handlebars templates to generate dynamic responses based on request data.
  • Fault injection: Simulate network delays, connection resets, and malformed responses for resilience testing.
  • Proxy mode: Record real API traffic and replay it as stubs.

Docker Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# docker-compose.yml
version: "3.8"

services:
  wiremock:
    image: wiremock/wiremock:3.5.4
    container_name: wiremock
    ports:
      - "8080:8080"
    volumes:
      - ./wiremock/mappings:/home/wiremock/mappings
      - ./wiremock/__files:/home/wiremock/__files
    command: --global-response-templating --verbose

Create a basic stub by placing a JSON file in wiremock/mappings/:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "request": {
    "method": "GET",
    "url": "/api/v1/users/42"
  },
  "response": {
    "status": 200,
    "bodyFileName": "user-42.json",
    "headers": {
      "Content-Type": "application/json"
    }
  }
}

And the response body goes in wiremock/__files/user-42.json:

1
2
3
4
5
6
{
  "id": 42,
  "name": "Jane Developer",
  "email": "jane@example.com",
  "role": "admin"
}

Start the server with docker compose up -d and test:

1
curl http://localhost:8080/api/v1/users/42

Dynamic Responses with Templating

WireMock’s Handlebars templating lets you build responses that adapt to the request:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "request": {
    "method": "POST",
    "url": "/api/v1/users"
  },
  "response": {
    "status": 201,
    "body": "{ \"id\": {{randomInt lower=1000 upper=9999}}, \"name\": \"{{jsonPath request.body '$.name'}}\", \"created\": \"{{now format='yyyy-MM-dd'}}\" }",
    "headers": {
      "Content-Type": "application/json"
    },
    "transformers": ["response-template"]
  }
}

Stateful Scenario Testing

WireMock scenarios let you model stateful API behavior:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "name": "Create then get user",
  "request": {
    "method": "POST",
    "url": "/api/v1/users"
  },
  "response": { "status": 201 },
  "scenarioName": "user-lifecycle",
  "requiredScenarioState": "Started",
  "newScenarioState": "user-created"
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "request": {
    "method": "GET",
    "url": "/api/v1/users/last"
  },
  "response": {
    "status": 200,
    "body": "{\"id\": 9001, \"name\": \"New User\"}"
  },
  "scenarioName": "user-lifecycle",
  "requiredScenarioState": "user-created"
}

The first stub only fires when the scenario state is Started and transitions it to user-created. The second stub only fires when the state is user-created. This models real API behavior where a GET after POST returns different data.

JUnit 5 Integration

For Java projects, embed WireMock directly in your test suite:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@ExtendWith(WireMockExtension.class)
class OrderServiceTest {

    @RegisterExtension
    static WireMockExtension wm = WireMockExtension.newInstance()
        .options(wireMockConfig().port(8080))
        .build();

    @Test
    void testGetOrder() {
        wm.stubFor(get(urlEqualTo("/api/orders/1"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"id\": 1, \"status\": \"shipped\"}")));

        OrderService service = new OrderService("http://localhost:8080");
        Order order = service.getOrder(1);

        assertEquals("shipped", order.getStatus());
    }
}

MockServer: The Contract Testing Specialist

MockServer takes a different approach. Instead of just stubbing responses, it lets you defiactualectations and verifies that your application actually sent the expected requests. This makes it ideal for contract testing between microservices.

Key Features

  • Expectation-based model: Define what requests you expect to receive, and MockServer verifies they arrive with the correct structure.
  • Verification API: After a test run, query MockServer to confirm specific requests were made.
  • WebSocket support: Mock WebSocket connections in addition to HTTP/HTTPS.
  • Port forwarding: Forward unmatched requests to a real backend (partial mocking).
  • OpenAPI integration: Generate expectations directly from OpenAPI/Swagger specifications.

Docker Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# docker-compose.yml
version: "3.8"

services:
  mockserver:
    image: mockserver/mockserver:5.15.0
    container_name: mockserver
    ports:
      - "1080:1080"
    environment:
      MOCKSERVER_WATCH_INITIALIZATION_JSON: "true"
      MOCKSERVER_INITIALIZATION_JSON_PATH: /config/expectations.json
      MOCKSERVER_LOG_LEVEL: INFO
    volumes:
      - ./mockserver/config:/config

Define expectations in mockserver/config/expectations.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[
  {
    "httpRequest": {
      "method": "GET",
      "path": "/api/products",
      "queryStringParameters": {
        "category": ["electronics"]
      }
    },
    "httpResponse": {
      "statusCode": 200,
      "headers": {
        "Content-Type": ["application/json"]
      },
      "body": "[{\"id\": 1, \"name\": \"Laptop\", \"price\": 999.99}, {\"id\": 2, \"name\": \"Phone\", \"price\": 699.99}]"
    }
  },
  {
    "httpRequest": {
      "method": "POST",
      "path": "/api/orders",
      "body": {
        "type": "JSON",
        "matchType": "STRICT",
        "json": "{\"productId\": 1, \"quantity\": 2}"
      }
    },
    "httpResponse": {
      "statusCode": 201,
      "body": "{\"orderId\": \"ORD-2026-001\", \"status\": \"confirmed\", \"total\": 1999.98}"
    }
  }
]

Verification in Tests

The real power of MockServer is verification. After your test runs, confirm the application made the correct API calls:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
MockServerClient client = new MockServerClient("localhost", 1080);

// Verify a specific request was made
client.verify(
    HttpRequest.request()
        .withMethod("POST")
        .withPath("/api/orders")
        .withBody("{\"productId\": 1, \"quantity\": 2}")
);

// Verify no requests were made to a sensitive endpoint
client.verify(
    HttpRequest.request()
        .withMethod("DELETE")
        .withPath("/api/admin/users"),
    VerificationTimes.exactly(0)
);

Using the REST API Directly

You can manage expectations over HTTP from any language:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Create an expectation
curl -X PUT http://localhost:1080/mockserver/expectation \
  -H "Content-Type: application/json" \
  -d '{
    "httpRequest": {
      "method": "GET",
      "path": "/api/health"
    },
    "httpResponse": {
      "statusCode": 200,
      "body": "{\"status\": \"ok\", \"version\": \"2.1.0\"}"
    }
  }'

# Verify recorded requests
curl http://localhost:1080/mockserver/verify \
  -H "Content-Type: application/json" \
  -d '{
    "httpRequest": {
      "method": "GET",
      "path": "/api/health"
    }
  }'

OpenAPI-Driven Mocking

If you have an OpenAPI 3.0 specification, MockServer can auto-generate expectations:

1
2
3
4
5
6
7
# docker-compose addition
services:
  mockserver:
    environment:
      MOCKSERVER_OPENAPI_SPECIFICATION_PATH: /config/openapi.yaml
    volumes:
      - ./api/openapi.yaml:/config/openapi.yaml

MockServer reads the spec and creates stubs for every defined endpoint with example responses pulled from the specification itself. This means your mocks stay in sync with your API documentation automatically.

Mockoon: The Developer-Friendly Desktop Tool

Mockoon is the simplest option to get started. It is a desktop application (built on Electron) with a graphical interface for creating and managing mock APIs. No JSON configuration files, no command-line flags — just point and click.

Key Features

  • GUI-first design: Create routes, set responses, and manage environments visually.
  • Data templating: Built-in Faker.js integration for generating realistic fake data.
  • Route reflection: Echo back request data for debugging.
  • Import/export: Share mock configurations as JSON files with your team.
  • Hot reload: Changes take effect immediately without restart.
  • CLI mode: Run headless mocks in CI/CD via the @mockoon/cli package.

Installation

Install the desktop app from the official releases page or use the CLI:

1
npm install -g @mockoon/cli

Quick Start with CLI

Create a mock environment file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
  "uuid": "env-001",
  "name": "E-commerce API",
  "endpointPrefix": "api",
  "port": 3000,
  "routes": [
    {
      "uuid": "route-001",
      "type": "http",
      "method": "get",
      "endpoint": "products",
      "responses": [
        {
          "statusCode": 200,
          "body": "[{\"id\": 1, \"name\": \"Wireless Mouse\", \"price\": 29.99}, {\"id\": 2, \"name\": \"Mechanical Keyboard\", \"price\": 89.99}]",
          "headers": [{ "key": "Content-Type", "value": "application/json" }]
        }
      ]
    },
    {
      "uuid": "route-002",
      "type": "http",
      "method": "get",
      "endpoint": "products/:id",
      "responses": [
        {
          "statusCode": 200,
          "body": "{\"id\": {{urlParam 'id'}}, \"name\": \"Product {{faker 'commerce.productName'}}\", \"price\": {{faker 'commerce.price'}}, \"inStock\": {{faker 'datatype.boolean'}}}",
          "headers": [{ "key": "Content-Type", "value": "application/json" }],
          "templating": true
        }
      ]
    }
  ]
}

Run it:

1
mockoon-cli start --data ./ecommerce-api.json

Test it:

1
2
curl http://localhost:3000/api/products
curl http://localhost:3000/api/products/5

Using Faker.js for Realistic Data

Mockoon’s built-in Faker.js support means you can generate rich, varied test data without writing any code:

1
2
3
4
{
  "body": "{\n  \"users\": [\n    {{#repeat 5}}\n    {\n      \"id\": {{@index}},\n      \"name\": \"{{faker 'person.fullName'}}\",\n      \"email\": \"{{faker 'internet.email'}}\",\n      \"address\": \"{{faker 'location.streetAddress'}}, {{faker 'location.city'}}\",\n      \"createdAt\": \"{{faker 'date.recent' days=30}}\"\n    }\n    {{/repeat}}\n  ]\n}",
  "templating": true
}

This generates a response with five random user records, each with realistic names, emails, addresses, and timestamps. Every request returns different data, which is great for testing how your frontend handles varied content.

Running in CI/CD with GitHub Actions

Mockoon’s CLI works in headless CI environments:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# .github/workflows/test.yml
name: API Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mockoon:
        image: mockoon/mock-server:latest
        ports:
          - 3000:3000
        volumes:
          - ./mocks:/data

    steps:
      - uses: actions/checkout@v4

      - name: Start Mockoon mock server
        run: |
          docker run -d --name mock-server \
            -p 3000:3000 \
            -v ${{ github.workspace }}/mocks:/data \
            mockoon/mock-server:latest \
            --data /data/api-mock.json

      - name: Run integration tests
        run: npm test
        env:
          API_BASE_URL: http://localhost:3000

Advanced: Combining Tools in a Microservice Architecture

When you have multiple services that depend on each other, a single mock server is not enough. Here is a realistic pattern using Docker Compose to mock an entire microservice stack:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# docker-compose.test.yml
version: "3.8"

services:
  # Mock the payment service
  payment-mock:
    image: wiremock/wiremock:3.5.4
    ports:
      - "8081:8080"
    volumes:
      - ./mocks/payment:/home/wiremock/mappings
    command: --global-response-templating

  # Mock the inventory service
  inventory-mock:
    image: mockserver/mockserver:5.15.0
    ports:
      - "8082:1080"
    environment:
      MOCKSERVER_INITIALIZATION_JSON_PATH: /config/inventory.json
    volumes:
      - ./mocks/inventory:/config

  # Mock the notification service
  notification-mock:
    image: mockoon/mock-server:latest
    ports:
      - "8083:3000"
    volumes:
      - ./mocks/notifications:/data
    command: --data /data/notifications.json

  # Your application under test
  app:
    build: .
    environment:
      PAYMENT_SERVICE_URL: http://payment-mock:8080
      INVENTORY_SERVICE_URL: http://inventory-mock:1080
      NOTIFICATION_SERVICE_URL: http://notification-mock:3000
    depends_on:
      - payment-mock
      - inventory-mock
      - notification-mock

This setup gives you deterministic responses from every dependency. Your application runs against mocks that behave consistently, test runs complete in seconds instead of minutes, and you never need a staging environment just to run integration tests.

Choosing the Right Tool

Choose WireMock if:

  • You are in the Java/JVM ecosystem and want native JUnit integration.
  • You need advanced request matching with regex, JSONPath, and XPath.
  • You want stateful scenarios for testing multi-step API flows.
  • You need GraphQL or gRPC support.
  • Your team already uses Java and wants a library they can embed.

Choose MockServer if:

  • You need contract testing between microservices.
  • You want to verify that your application makes the correct API calls.
  • You have an OpenAPI spec and want auto-generated mocks.
  • You need WebSocket mocking alongside HTTP.
  • Your team works in multiple languages and prefers the REST API approach.

Choose Mockoon if:

  • You want a visual interface with zero configuration.
  • You are a solo developer or small team prototyping quickly.
  • You need realistic fake data via Faker.js without writing templates.
  • You prefer a desktop app for day-to-day mock management.
  • You want to share mock environments with non-technical teammates.

Performance Comparison

In benchmark tests running 10,000 sequential requests on a modern laptop (M2 chip, 16 GB RAM):

ToolAvg Response TimeRequests/secMemory Usage
WireMock (standalone)2.1 ms4,760~180 MB
MockServer (standalone)2.8 ms3,570~220 MB
Mockoon (CLI)3.5 ms2,850~120 MB

All three are fast enough for development and testing. The differences only matter at very high request volumes, where WireMock’s optimized engine has a slight edge.

Conclusion

Self-hosted API mocking is not a luxury — it is a productivity multiplier. Whether you are building a frontend before the backend exists, testing microservice integrations, or running CI pipelines that need to be fast and reliable, having a local mock server pays for itself in saved time.

WireMock remains the gold standard for Java teams and enterprise CI pipelines. MockServer excels at contract testing and verification. Mockoon is the fastest way to get a mock API running with zero code.

Pick the tool that matches your workflow, spin up a Docker container, and stop waiting on dependencies that are not ready yet. Your future self will thank you.

Frequently Asked Questions (FAQ)

Which one should I choose in 2026?

The best choice depends on your specific requirements:

  • For beginners: Start with the simplest option that covers your core use case
  • For production: Choose the solution with the most active community and documentation
  • For teams: Look for collaboration features and user management
  • For privacy: Prefer fully open-source, self-hosted options with no telemetry

Refer to the comparison table above for detailed feature breakdowns.

Can I migrate between these tools?

Most tools support data import/export. Always:

  1. Backup your current data
  2. Test the migration on a staging environment
  3. Check official migration guides in the documentation

Are there free versions available?

All tools in this guide offer free, open-source editions. Some also provide paid plans with additional features, priority support, or managed hosting.

How do I get started?

  1. Review the comparison table to identify your requirements
  2. Visit the official documentation (links provided above)
  3. Start with a Docker Compose setup for easy testing
  4. Join the community forums for troubleshooting
Advertise here