Skip to content

WireMock Runner Hosting

This guide provides step-by-step instructions for configuring WireMock Runner hosting in your Aspire AppHost. WireMock Runner orchestrates multiple WireMock mock services as a single managed resource, allowing you to pull mock definitions from WireMock Cloud using API tokens.

Experimental Feature

WireMock Runner APIs are marked as experimental (SAIFMOCK001). See SAIFMOCK001 diagnostic for suppression options.

📚 Useful Resources

📋 Prerequisites

Before you begin, ensure you have:

  • ✅ .NET 10.0 SDK installed
  • ✅ Docker Desktop running
  • ✅ Node.js and npm installed (required for the provision-and-pull phase; used to install the WireMock CLI automatically)
  • ✅ An API project with 1 or more external API dependencies you wish to mock
  • ✅ WireMock Cloud account and API token
  • ✅ WireMock Cloud project(s) with mocks defined (or use auto-provisioning)

📁 Projects

  • AppHost Project: Contains logic for setting up your application and dependencies

🛠️ WireMock Runner - Aspire Hosting Extensions

Overview

The WireMock Runner namespace (SAIF.Platform.Aspire.Hosting.WiremockRunner) provides Aspire hosting extensions for orchestrating multiple WireMock mock services as a single managed resource. This enables you to:

  • Manage multiple mock services through a single runner resource
  • Pull mock definitions from WireMock Cloud using API tokens
  • Auto-provision new mock APIs in WireMock Cloud with seed stubs
  • Integrate with Aspire's service discovery
  • Organize mock configurations in a dedicated directory
  • Iterate on mock stubs without restarting the entire AppHost

For a detailed comparison with WireMock CLI and other approaches, see Comparison with Other Mocking Approaches.


🚀 Step-by-Step Implementation

Step 1: Add Package Reference

Add a reference to the SAIF.Platform.Aspire.Hosting NuGet package in your AppHost project:

<ItemGroup>
  <PackageReference Include="SAIF.Platform.Aspire.Hosting" />
</ItemGroup>

Step 2: Suppress Experimental Warning

Since WireMock Runner is experimental, you'll need to suppress the SAIFMOCK001 warning. See SAIFMOCK001 diagnostic documentation for suppression options.

Step 3: Add WireMock Runner Resource

Create the WireMock Runner resource. The API token is automatically configured:

var wiremockRunner = builder.AddWiremockRunner("wiremock");

API Token Handling:

  • Local Development: Prompts for token as a secret parameter in the Aspire dashboard
  • CI/CD (when CI=true env var is set): Uses WMC_API_TOKEN environment variable directly (no parameter prompt)

To obtain your WireMock Cloud API token:

  1. Log in to WireMock Cloud
  2. Navigate to SettingsAPI Tokens
  3. Create a new API token or copy an existing one

CI/CD Configuration: Set the WMC_API_TOKEN environment variable in your CI/CD pipeline using integrationTestEnvironmentVariables:

extends:
  template: azure-dotnet-api-pr-v3.yml@templates
  parameters:
    # ... other parameters ...
    integrationTestEnvironmentVariables:
      - name: WMC_API_TOKEN
        value: $(WmcApiToken)  # From variable group/secret

See Integration Test Environment Variables for full parameter documentation.

Step 4: Add Mock Services

Add individual mock services to the runner. Each mock service can either reference an existing WireMock Cloud project or be provisioned automatically:

// Reference an existing WireMock Cloud project (using project ID)
var externalApi = wiremockRunner.AddMock(
    name: "external-api",
    port: 8080,
    wiremockProjectId: "abc123xyz"
);

// Add mock service that will be created in WireMock Cloud (no project ID)
// The provision resource will create the mock API and set organization-wide access
var newApi = wiremockRunner.AddMock(
    name: "new-api",
    port: 8082
);

Finding Your WireMock Project ID (for existing mocks):

  1. Navigate to your mock in WireMock Cloud
  2. Copy the URL from your browser: https://app.wiremock.cloud/mock-apis/abc123xyz/stubs/...
  3. The wiremockProjectId is the unique identifier in the URL (e.g., abc123xyz)

Step 5: Add Seed Stubs (Optional)

For auto-provisioned mocks, provide initial stub data so the mock is immediately useful:

var newApi = wiremockRunner.AddMock("new-api", 8082)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "new-api"));

Create a stubs.json file in the seed directory using the WireMock export format:

seeds/new-api/stubs.json
{
  "mappings": [
    {
      "id": "a1b2c3d4-0001-4000-8000-000000000001",
      "name": "List Animals",
      "request": { "method": "GET", "url": "/animals" },
      "response": {
        "status": 200,
        "jsonBody": [
          { "name": "dog", "type": "domestic" },
          { "name": "cat", "type": "domestic" }
        ]
      }
    }
  ]
}

Stable Stub IDs

Include a stable id (UUID) for each mapping. This makes imports idempotent — re-running provision-and-pull updates existing stubs rather than creating duplicates.

Step 6: Reference Mocks in Your Application

Wire your application to reference the mock services:

builder
    .AddProject<Projects.MyApiService>("my-api-service")
    .WithReference(externalApi)
    .WithReference(newApi)
    .WaitFor(externalApi)
    .WaitFor(newApi);

📝 Complete Example

Here's a complete AppHost configuration using WireMock Runner:

AppHost.cs
using SAIF.Platform.Aspire.Hosting.WiremockRunner;

var builder = DistributedApplication.CreateBuilder(args);

// Create WireMock Runner resource
// - Local dev: prompts for token in Aspire dashboard
// - CI/CD (CI=true): reads from WMC_API_TOKEN environment variable
var wiremockRunner = builder.AddWiremockRunner("wiremock");

// Reference existing WireMock Cloud projects
var customerApi = wiremockRunner.AddMock("customer-api", 8080, "abc123xyz");
var paymentApi = wiremockRunner.AddMock("payment-api", 8081, "def456uvw");

// Auto-provision a new mock with seed stubs
var inventoryApi = wiremockRunner.AddMock("inventory-api", 8082)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "inventory-api"));

// Wire application to mock services
builder
    .AddProject<Projects.MyApiService>("my-api-service")
    .WithReference(customerApi)
    .WithReference(paymentApi)
    .WithReference(inventoryApi)
    .WaitFor(customerApi)
    .WaitFor(paymentApi)
    .WaitFor(inventoryApi);

builder.Build().Run();

🔍 How It Works

Architecture

┌─────────────────────────────────────────────────────────┐
│                   WireMock Runner                        │
│                                                          │
│  ┌────────────────────────────────────────────────────┐ │
│  │  wiremock-provision-and-pull  (runs on start)      │ │
│  │                                                     │ │
│  │  Phase 1: Provision                                 │ │
│  │  - Creates mock APIs in WireMock Cloud              │ │
│  │  - Grants org-wide ACL (mock_api_editor)            │ │
│  │  - Saves project IDs to .provisioned.json           │ │
│  │                                                     │ │
│  │  Phase 1.5: Import Seeds                            │ │
│  │  - Uploads stubs.json to WireMock Cloud             │ │
│  │  - Uses wiremock import CLI command                  │ │
│  │                                                     │ │
│  │  Phase 2: Pull                                      │ │
│  │  - Downloads all mock stubs from WireMock Cloud     │ │
│  │  - Saves to .wiremock/ directory as YAML            │ │
│  │                                                     │ │
│  │  Phase 2.5: Fix Ports                               │ │
│  │  - Corrects ports in wiremock.yaml to match config  │ │
│  └────────────────────┬───────────────────────────────┘ │
│               starts after completion                    │
│                        ▼                                 │
│  ┌────────────────────────────────────────────────────┐ │
│  │  wiremock-runner  (container)                      │ │
│  │  ┌──────────────┐  ┌──────────────┐               │ │
│  │  │ Mock Service │  │ Mock Service │  ...           │ │
│  │  │ Port: 8080   │  │ Port: 8082   │               │ │
│  │  └──────────────┘  └──────────────┘               │ │
│  └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│              Your Application Service                    │
│  - References mock services via WithReference()          │
│  - Uses Aspire service discovery                         │
└─────────────────────────────────────────────────────────┘

Startup Flow

When the AppHost starts:

  1. Provision-and-Pull runs automatically:
  2. Phase 1: Creates any auto-provisioned mock APIs in WireMock Cloud (idempotent — skips if already created)
  3. Phase 1.5: Imports seed stubs from WithSeeds() directories into WireMock Cloud
  4. Phase 2: Pulls all mock stubs from WireMock Cloud to the .wiremock/ directory
  5. Phase 2.5: Fixes ports in wiremock.yaml to match the configured mock ports
  6. Runner Container starts after provision-and-pull completes, serving stubs from the .wiremock/ bind mount
  7. Mock resources become Running in the Aspire dashboard with allocated endpoints
  8. Your application starts after mocks are ready (via WaitFor())

Inner Loop: Editing Stubs

To iterate on mock stubs without restarting the entire AppHost:

  1. Edit the stubs.json file in your seed directory
  2. Re-run the wiremock-provision-and-pull resource from the Aspire dashboard (click Start)
  3. Runner auto-restarts — the runner container is automatically restarted via docker restart after provision-and-pull completes on subsequent runs
  4. Verify restart — check the logs of wiremock-runner in the Aspire dashboard for startup messages matching your re-run timestamp

Confirming the restart

The runner resource stays in the Running state throughout the container restart, so dashboard status alone doesn't indicate completion. Check the wiremock-runner logs for the WireMock startup banner to confirm new stubs are loaded.

Why docker restart?

WireMock Runner loads stubs at container startup from the bind-mounted .wiremock/ directory. Updating files on disk alone does not trigger an in-memory reload. The WireMock admin API reset endpoints (POST /__admin/mappings/reset) only reset to the startup state, not the current disk state. A container restart is the only reliable way to pick up changes.

Directory Structure

The WireMock Runner organizes mock files in a structured directory:

.wiremock/
├── .manifest.json                # Mock metadata (generated)
├── .provisioned.json             # Provisioned project IDs (generated)
├── wiremock.yaml                 # WireMock Runner configuration (generated)
├── customer-api/
│   ├── mappings/
│   │   └── stub-mappings.yaml
│   └── __files/
├── inventory-api/
│   ├── mappings/
│   │   └── stub-mappings.yaml
│   └── __files/
└── payment-api/
    ├── mappings/
    │   └── stub-mappings.yaml
    └── __files/

Seed Directory Structure

Seed stubs are stored alongside the AppHost project:

AppHost/
├── AppHost.cs
├── seeds/
│   └── inventory-api/
│       └── stubs.json            # WireMock export format
└── .wiremock/                     # Generated by provision-and-pull

🎯 Usage Scenarios

Scenario 1: Multi-Service Development

When developing a service that depends on multiple external APIs:

var wiremockRunner = builder.AddWiremockRunner("wiremock");

// Mock all external dependencies
var customerApi = wiremockRunner.AddMock("customer-api", 8080, "proj1");
var inventoryApi = wiremockRunner.AddMock("inventory-api", 8081, "proj2");
var shippingApi = wiremockRunner.AddMock("shipping-api", 8082, "proj3");

// Your order service references all mocks
builder.AddProject<Projects.OrderService>("order-service")
    .WithReference(customerApi)
    .WithReference(inventoryApi)
    .WithReference(shippingApi);

Scenario 2: Rapid Prototyping with Seeds

Start a new mock from scratch with seed data:

var notificationApi = wiremockRunner.AddMock("notification-api", 8083)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "notification-api"));
seeds/notification-api/stubs.json
{
  "mappings": [
    {
      "id": "b2c3d4e5-0001-4000-8000-000000000001",
      "name": "Send Notification",
      "request": { "method": "POST", "url": "/notifications" },
      "response": { "status": 202, "jsonBody": { "status": "queued" } }
    }
  ]
}

Edit the stubs, re-run provision-and-pull from the dashboard, and see changes immediately.

Scenario 3: Team Collaboration

Share mock definitions across the team using WireMock Cloud:

  1. Developer A creates/updates mocks in WireMock Cloud (or edits seed stubs and re-runs provision-and-pull)
  2. Developer B pulls latest mocks by restarting the AppHost
  3. CI/CD uses the same WireMock Cloud projects for integration tests

🔧 Configuration Options

AddWiremockRunner()

Creates the WireMock Runner resource with automatic API token handling. See Step 3 for token configuration details.

var wiremockRunner = builder.AddWiremockRunner("wiremock");

WithDirectory() (Optional)

Specifies the local directory for storing mock configurations (default: ./.wiremock):

var wiremockRunner = builder.AddWiremockRunner("wiremock")
    .WithDirectory("./mocks"); // Custom directory (optional)

AddMock()

Adds a mock service to the runner. Two modes are supported:

Reference an existing WireMock Cloud project:

var mock = wiremockRunner.AddMock(
    name: "service-name",           // Service discovery name
    port: 8080,                     // Container target port
    wiremockProjectId: "abc123xyz"  // WireMock Cloud project ID
);

Provision a new mock API in WireMock Cloud:

var mock = wiremockRunner.AddMock(
    name: "service-name",           // Service discovery name + WireMock Cloud mock name
    port: 8080                      // Container target port
);

When wiremockProjectId is omitted, the provision-and-pull resource will:

  1. Create the mock API in WireMock Cloud via the REST API (POST /v1/mock-apis)
  2. Grant organization-wide access with mock_api_editor role
  3. Save the created project ID to .wiremock/.provisioned.json
  4. Import any seed stubs, then pull all stubs to disk

Provisioned project IDs persist in .provisioned.json so mock APIs are only created once.

WithSeeds()

Specifies a directory containing a stubs.json file for initial stub data. Only applies to auto-provisioned mocks (those without a wiremockProjectId). See Step 5 for format details and best practices.

var mock = wiremockRunner.AddMock("my-service", 8082)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "my-service"));

🐛 Troubleshooting

Issue: Mocks Not Loading

Symptoms: Mock services start but don't respond to requests

Solutions:

  1. Verify the WireMock project ID is correct
  2. Check that the API token has access to the project
  3. Inspect the .wiremock directory to ensure files were downloaded
  4. Check the Aspire dashboard for runner errors

Issue: Authentication Failed

Symptoms: Error messages about invalid API token

Solutions:

  1. Local Development: Enter the correct token when prompted in the Aspire dashboard
  2. CI/CD: Verify the WMC_API_TOKEN environment variable is set correctly
  3. Ensure the token hasn't expired in WireMock Cloud

Issue: Port Conflicts

Symptoms: Mock services fail to start due to port in use

Solutions:

  1. Ensure each mock uses a unique port
  2. Check for other processes using the same ports
  3. Use higher port numbers (e.g., 8080+) to avoid conflicts

Issue: Service Discovery Not Working

Symptoms: Application can't resolve mock service endpoints

Solutions:

  1. Ensure you're using .WithReference(mockResource) in your application
  2. Verify the mock name matches what you're requesting
  3. Check the Aspire dashboard to confirm service registration

Issue: Stub Changes Not Reflected

Symptoms: Edited stubs.json but mock still returns old data

Solutions:

  1. Verify the file was saved
  2. Re-run the wiremock-provision-and-pull resource from the Aspire dashboard — the runner container restarts automatically
  3. If auto-restart fails, manually restart the wiremock-runner container from the dashboard

Note

WireMock Runner only reads stubs at container startup. Editing files on disk requires a container restart to take effect.


📊 Comparison with Other Mocking Approaches

Approach WireMock Runner WireMock CLI Container-based
Runtime Container (Docker) Executable Container
CI/Integration Tests ✅ Supported ❌ Not supported ✅ Supported
Setup Complexity Medium Low Low
Authentication API token (automatic) Optional Not applicable
Orchestration Centralized Per-mock Per-container
Seed Stubs ✅ WithSeeds() ❌ N/A ⚠️ Manual setup
Team Sharing ✅ Via WireMock Cloud ✅ Via WireMock Cloud ⚠️ Manual file sharing
Multi-Mock ✅ Single resource ⚠️ Multiple resources ⚠️ Multiple containers
Production Ready ⚠️ Experimental ✅ Stable ✅ Stable

🎓 Best Practices

Organize Mock Projects by Domain

Create separate WireMock Cloud projects for different domains:

  • customer-service-mocks
  • payment-service-mocks
  • inventory-service-mocks

Use Descriptive Mock Names

Choose names that clearly indicate what's being mocked:

var customerApiMock = wiremockRunner.AddMock("customer-api", 8080, "proj1");
// Not: var mock1 = wiremockRunner.AddMock("mock1", 8080, "proj1");

Document Project IDs

Document which WireMock Cloud project ID corresponds to which service:

// WireMock Cloud Project IDs:
// - abc123xyz: Customer API (https://app.wiremock.cloud/mock-apis/abc123xyz)
// - def456uvw: Payment API (https://app.wiremock.cloud/mock-apis/def456uvw)
var customerApi = wiremockRunner.AddMock("customer-api", 8080, "abc123xyz");
var paymentApi = wiremockRunner.AddMock("payment-api", 8081, "def456uvw");

When to Use WireMock Runner vs. CLI

Use WireMock Runner when you need integration tests in CI/CD, container-based orchestration, centralized multi-mock management, or seed stubs for rapid prototyping.

Use WireMock CLI when you need a stable solution for local-only development with simple, independent mock scenarios and minimal setup.



💬 Feedback

WireMock Runner is an experimental feature. Please provide feedback through:

  • 🐛 Issues for bugs or problems
  • 💡 Feature requests for improvements
  • 📝 Discussions for general feedback

Visit the Forge repository to share your thoughts.