Remote Work Tools

API mocking lets remote teams work in parallel without waiting for backend endpoints. The frontend team mocks the agreed API contract; the backend team implements against the same spec. When they integrate, surprises are minimal because the contract was validated throughout development.


WireMock (Java/Docker, Production-Grade)

WireMock is the most capable mock server for integration testing. It runs as a Docker container and supports request matching, response templating, stateful scenarios, and fault injection.

# docker-compose.yml
version: "3.8"
services:
  wiremock:
    image: wiremock/wiremock:3.x
    container_name: wiremock
    ports:
      - "8080:8080"
    volumes:
      - ./wiremock/mappings:/home/wiremock/mappings
      - ./wiremock/__files:/home/wiremock/__files
    command: --verbose --global-response-templating

Define a stub in wiremock/mappings/users.json:

{
  "mappings": [
    {
      "request": {
        "method": "GET",
        "urlPathPattern": "/api/users/[0-9]+"
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json"
        },
        "jsonBody": {
          "id": "{{request.pathSegments.[2]}}",
          "name": "Test User",
          "email": "user-{{request.pathSegments.[2]}}@example.com",
          "plan": "pro",
          "createdAt": "{{now format='yyyy-MM-dd'}}"
        }
      }
    },
    {
      "request": {
        "method": "POST",
        "url": "/api/users",
        "bodyPatterns": [
          {
            "matchesJsonPath": "$.email"
          }
        ]
      },
      "response": {
        "status": 201,
        "headers": {
          "Content-Type": "application/json",
          "Location": "/api/users/{{randomValue type='UUID'}}"
        },
        "jsonBody": {
          "id": "{{randomValue type='UUID'}}",
          "email": "{{jsonPath request.body '$.email'}}",
          "createdAt": "{{now}}"
        }
      }
    }
  ]
}

Test error scenarios with fault injection:

{
  "request": {
    "method": "GET",
    "url": "/api/payment",
    "headers": {
      "X-Simulate-Error": { "equalTo": "timeout" }
    }
  },
  "response": {
    "fault": "CONNECTION_RESET_BY_PEER"
  }
}

Mockoon (Desktop + CLI, Zero-Config)

Mockoon is the fastest way to spin up a mock API. Import an OpenAPI spec and it generates stubs automatically.

# Install CLI
npm install -g @mockoon/cli

# Start a mock environment from file
mockoon-cli start --data ./mockoon-environment.json --port 3001

# Start from OpenAPI spec
mockoon-cli start \
  --data https://api.yourcompany.com/openapi.json \
  --port 3001

Define environment rules in JSON (or use the desktop GUI and export):

{
  "uuid": "abc123",
  "name": "Payments API Mock",
  "port": 3001,
  "routes": [
    {
      "method": "post",
      "endpoint": "payments/charge",
      "responses": [
        {
          "statusCode": 200,
          "label": "Successful charge",
          "headers": [
            { "key": "Content-Type", "value": "application/json" }
          ],
          "body": "{\n  \"id\": \"ch_{{faker 'string.uuid'}}\",\n  \"amount\": {{body 'amount'}},\n  \"currency\": \"{{body 'currency'}}\",\n  \"status\": \"succeeded\"\n}",
          "rules": [],
          "default": true
        },
        {
          "statusCode": 402,
          "label": "Card declined",
          "body": "{\"error\": \"card_declined\", \"message\": \"Your card was declined.\"}",
          "rules": [
            {
              "target": "body",
              "modifier": "card_number",
              "value": "4000000000000002",
              "invert": false,
              "operator": "equals"
            }
          ],
          "default": false
        }
      ]
    }
  ]
}

Docker Compose for shared team mock:

services:
  api-mock:
    image: mockoon/cli:latest
    volumes:
      - ./mockoon:/data
    command: --data /data/environment.json --port 3001
    ports:
      - "3001:3001"

Prism (OpenAPI Contract Validation)

Prism by Stoplight serves mocks from OpenAPI specs and validates requests/responses against the schema. It catches contract violations at dev time.

npm install -g @stoplight/prism-cli

# Serve a mock from OpenAPI spec
prism mock https://api.yourcompany.com/openapi.yaml

# Or from a local file
prism mock ./api/openapi.yaml --port 4010

# Proxy real API but validate responses
prism proxy https://api.yourcompany.com ./api/openapi.yaml --port 4011

Prism returns errors when your OpenAPI spec and actual API diverge:

[PRISM] POST /payments/charge  ✓  Request valid
[PRISM] POST /payments/charge  ✗  Response invalid: missing required field 'transactionId'

Combine with CI to validate your API matches the spec on every deploy:

# .github/workflows/api-contract.yml
name: API Contract Tests
on: [push, pull_request]
jobs:
  contract:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Prism
        run: npm install -g @stoplight/prism-cli
      - name: Start API
        run: docker-compose up -d api
      - name: Validate responses against OpenAPI spec
        run: |
          prism proxy http://localhost:8080 ./api/openapi.yaml --port 4011 &
          sleep 3
          # Run integration test suite against Prism proxy
          npm run test:integration -- --baseUrl http://localhost:4011

MSW (Mock Service Worker, Frontend-First)

MSW intercepts requests at the network level using a Service Worker in the browser or Node.js interceptors in tests. The same mock definitions work in both environments.

npm install msw --save-dev
npx msw init public/

Define handlers:

// src/mocks/handlers.js
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users/:id', ({ params }) => {
    return HttpResponse.json({
      id: params.id,
      name: 'Alice Test',
      email: `user-${params.id}@example.com`,
    });
  }),

  http.post('/api/payments/charge', async ({ request }) => {
    const body = await request.json();

    // Simulate card decline
    if (body.cardNumber === '4000000000000002') {
      return HttpResponse.json(
        { error: 'card_declined', message: 'Your card was declined.' },
        { status: 402 }
      );
    }

    return HttpResponse.json({
      id: `ch_${Math.random().toString(36).slice(2)}`,
      amount: body.amount,
      status: 'succeeded',
    });
  }),
];

Browser setup:

// src/mocks/browser.js
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);
// src/main.jsx
if (process.env.NODE_ENV === 'development') {
  const { worker } = await import('./mocks/browser');
  await worker.start({ onUnhandledRequest: 'warn' });
}

Node.js test setup (Jest/Vitest):

// src/setupTests.js
import { server } from './mocks/server';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Override handlers per-test for error scenarios:

import { server } from '../mocks/server';
import { http, HttpResponse } from 'msw';

test('shows error message when payment fails', async () => {
  server.use(
    http.post('/api/payments/charge', () => {
      return HttpResponse.json({ error: 'network_error' }, { status: 500 });
    })
  );

  // ... render and assert
});

Tool Selection Guide

Tool Best For Strength
WireMock Integration/E2E testing Advanced matching, fault simulation
Mockoon Rapid prototyping, shared team mock Zero config, GUI included
Prism Contract validation OpenAPI spec enforcement
MSW Frontend development and unit tests Network-level interception, no proxy

For most remote teams: use MSW for frontend unit tests, Mockoon for shared team mocks during API development, and WireMock for integration test suites in CI.