How SapMock Works

A comprehensive guide to understanding the architecture, capabilities, and limitations of SapMock.

What is SapMock?

SapMock is a standalone HTTP proxy server that intercepts requests to SAP OData services and returns realistic mock data based on the service's metadata schema.

It sits between your application and your SAP system, allowing you to develop and test SAP integrations without depending on live SAP environments.

Think of it as a "smart proxy" that learns your SAP service structure and automatically generates type-correct, realistic mock responses.

How Does It Work?

1

Proxy Interception

Your application makes OData requests to SapMock instead of directly to SAP:

Before: http://sap-server:8000/sap/opu/odata/...
After: http://localhost:5000/sap/opu/odata/...

SapMock intercepts all requests matching the pattern /sap/opu/odata/*

2

Metadata Discovery (Two Ways)

SapMock supports two methods for loading metadata:

Option 1: Automatic Fetching from SAP

  • On first request, fetches $metadata from your real SAP system
  • Parses EDMX and caches indefinitely
  • Requires SAP connection + authentication

Option 2: POST EDMX Directly

  • Send EDMX XML in POST/PUT request body (Content-Type: application/xml or text/xml)
  • SapMock parses and caches it for that service path
  • No SAP connection needed - works completely offline!
POST /sap/opu/odata/iwbep/gwsample_basic/ProductSet Content-Type: application/xml <?xml version="1.0"?> <edmx:Edmx Version="1.0" xmlns:edmx="..."> <!-- Your EDMX metadata here --> </edmx:Edmx>

What gets extracted: Entity types, properties, EDM data types, navigation properties, complex types, and relationships.

3

Entity Matching & Routing

SapMock uses 11 intelligent matching strategies (executed in order) to find the correct entity:

1. Exact match (case-sensitive)
2. Add "Type" suffix
3. Remove "Type" suffix
4. Case-insensitive exact match
5. Case-insensitive + "Type" suffix
6. Plural to singular (remove 's')
7. Singular to plural (add 's')
8. EntitySet pattern (remove "Set"/"Collection")
9. Partial match (contains)
10. Reverse partial match
11. Fuzzy match (Levenshtein, 80%+ similarity)

Result: Entity resolution is highly resilient - the multi-strategy approach maximizes match success even with naming variations.

4

Mock Data Generation

SapMock generates realistic, type-correct mock data using the Bogus library with intelligent heuristics:

String properties → Smart detection (names, emails, addresses, etc.)
Numeric properties → Contextual values (prices, quantities, IDs)
DateTime properties → SAP format (/Date(timestamp)/)
Boolean properties → Random true/false
Navigation properties → Nested objects with circular reference protection
Complex types → Recursively generated structures

Important: Mock data is generated on-the-fly for each request. No state is maintained between requests (stateless design).

5

OData Response Formatting

The generated mock data is formatted as a proper OData response:

{ "d": { "results": [ { "__metadata": { "uri": "http://localhost:5000/.../ProductSet('001')", "type": "ProductType" }, "ProductID": "001", "ProductName": "Ergonomic Steel Chair", "Price": "299.99", "Currency": "USD", "Category": { ... } } ] } }

Includes proper __metadata fields, OData V2/V3/V4 envelope formats, and correct MIME types.

Authentication & Security

Two Authentication Modes

SapMock supports two flexible authentication modes with automatic fallback, giving you complete control over how credentials are managed.

1

Header Forwarding (Recommended)

Authentication headers from your requests are transparently forwarded to SAP. No credentials stored in SapMock.

// Your app makes requests with your own auth headers fetch("http://localhost:5000/sap/opu/odata/.../ProductSet", { headers: { "Authorization": "Basic " + btoa("user:pass"), "sap-client": "100" } }); // SapMock forwards these headers to your real SAP server // when fetching $metadata, then returns mock data
  • Zero-trust security - No credentials stored in SapMock
  • User-specific data - Each developer uses their own SAP credentials
  • Works with any auth - OAuth, Basic, Bearer tokens, SAML, or custom headers

Forwarded headers: Authorization, sap-client, and sap-language. These headers are forwarded to SAP when fetching metadata.

2

Configured Credentials (Fallback)

If no Authorization header is present, SapMock falls back to configured credentials.

// Docker docker run -p 5000:5000 \ -e SAP_HOST=https://sap-server:8000 \ -e SAP_USERNAME=MOCKUSER \ -e SAP_PASSWORD=your-password \ -e SAP_CLIENT=100 \ sapmock:latest // Or appsettings.json { "Sap": { "Host": "https://sap-server:8000", "Username": "MOCKUSER", "Password": "your-password", "Client": "100" } }
  • Useful for shared CI/CD environments with a dedicated mock SAP account
  • Credentials are optional - header forwarding works without them

Security Warning: Never commit credentials to source control. Use environment variables or secret managers (Azure Key Vault, AWS Secrets Manager).

X-CSRF-Token Handling

SapMock includes automatic X-CSRF-Token generation to satisfy SAP client libraries that require these tokens.

How It Works

  • 1.When a request includes x-csrf-token: fetch header, SapMock generates a random Base64 token (32 bytes, SAP-style format)
  • 2.The token is returned in the response header: x-csrf-token: [generated-token]
  • 3.If no x-csrf-token header exists in the response, SapMock automatically adds one to every response
  • 4.Tokens are NOT validated - SapMock accepts any token value since responses are stateless mocks
// Example: CSRF token fetch GET /sap/opu/odata/iwbep/gwsample_basic/ProductSet Headers: x-csrf-token: fetch // Response includes: x-csrf-token: vK8j3nP9xQ...== (44-char Base64 string) // Tokens are randomly generated each time // No validation occurs - any token is accepted

Why this matters: Many SAP OData clients (SAP UI5, SAP Cloud SDK) automatically fetch CSRF tokens before making write requests. SapMock generates these tokens so client libraries work without modification, even though SapMock doesn't actually accept write operations.

What SapMock Supports

Supported Features

  • Automatic metadata fetching from SAP $metadata endpoint
  • POST/PUT EDMX XML in request body for offline use (no SAP connection required)
  • $metadata requests (returns cached EDMX XML)
  • Entity collections (e.g., /ProductSet)
  • Single entity by key (e.g., /ProductSet('001'))
  • OData V2, V3, and V4 protocol versions
  • Complex types and nested structures
  • Navigation properties (generated recursively)
  • Header forwarding (Authorization, sap-client, sap-language)
  • X-CSRF-Token generation for SAP client compatibility

NOT Supported (By Design)

  • Query parameters ($filter, $select, $expand, $skip, $top, $orderby)
  • State management (no data persistence between requests)
  • POST/PUT/PATCH/DELETE operations (read-only mock)
  • Function imports or custom operations
  • Batch requests ($batch endpoint)
  • Recording/replay functionality
  • User-defined responses or custom mock data

Why? SapMock is designed for fast, stateless metadata-based mocking only. Query parameters and state management would require a database and complex query engine.

Ideal Use Cases

✅ Perfect For

  • Backend integration tests that verify entity structures and data types
  • CI/CD pipelines where SAP is unavailable or unreliable
  • Local development without VPN or SAP access
  • Contract testing to ensure your code handles OData responses correctly
  • Learning OData without setting up a real SAP system

❌ NOT Suitable For

  • Testing query logic ($filter, $select, pagination)
  • Testing CRUD operations (create, update, delete)
  • Validating business logic in SAP (e.g., price calculations, stock checks)
  • Load testing SAP-specific performance
  • Replacing a full SAP sandbox for end-to-end testing

Quick Start Example

1. Start SapMock with Docker

docker run -p 5000:5000 \ -e SAP_HOST=https://your-sap-server:8000 \ sapmock:latest

2. Point your app to SapMock

// Before const baseUrl = "https://sap-server:8000"; // After const baseUrl = "http://localhost:5000";

3. Make requests as usual

fetch("http://localhost:5000/sap/opu/odata/iwbep/gwsample_basic/ProductSet") .then(res => res.json()) .then(data => console.log(data)); // SapMock automatically: // 1. Fetches metadata from your SAP server // 2. Generates realistic Product mock data // 3. Returns proper OData response

That's it!

No configuration files, no manual JSON responses, no complex setup. SapMock learns from your SAP metadata and just works.

Production Deployment

SapMock.Service can be deployed anywhere: Docker, Kubernetes, Azure, AWS, on-premises, or integrated into your CI/CD pipeline. Choose the deployment method that fits your infrastructure.

Docker

Run as a container (recommended for production)

Using docker run

docker run -d \ --name sapmock-service \ -p 5000:8080 \ -e SAPMOCK_LICENSE="your-license-key" \ -e Sap__Host="https://sap.company.com:8000" \ -e Sap__ValidateSsl="true" \ sapmock/service:latest

Using docker-compose.yml

version: '3.8' services: sapmock-service: image: sapmock/service:latest container_name: sapmock-service ports: - "5000:8080" environment: - SAPMOCK_LICENSE=your-license-key - Sap__Host=https://sap.company.com:8000 - Sap__ValidateSsl=true restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 3s retries: 3

Kubernetes

Deploy to any Kubernetes cluster (AKS, EKS, GKE, on-premises)

1. Create Secret for License Key

kubectl create secret generic sapmock-license \ --from-literal=license='your-license-key'

2. Create Deployment

apiVersion: apps/v1 kind: Deployment metadata: name: sapmock-service spec: replicas: 2 selector: matchLabels: app: sapmock-service template: metadata: labels: app: sapmock-service spec: containers: - name: sapmock-service image: sapmock/service:latest ports: - containerPort: 8080 name: http env: - name: SAPMOCK_LICENSE valueFrom: secretKeyRef: name: sapmock-license key: license - name: Sap__Host value: "https://sap.company.com:8000" - name: Sap__ValidateSsl value: "true" - name: ASPNETCORE_URLS value: "http://+:8080" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10 resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "1000m" --- apiVersion: v1 kind: Service metadata: name: sapmock-service spec: selector: app: sapmock-service ports: - port: 80 targetPort: 8080 name: http type: ClusterIP

3. Deploy

kubectl apply -f deployment.yaml

Standalone Binary

Run without Docker on Windows, Linux, or macOS

Download Binaries

Download the latest release:

Download Page

Windows: sapmock-service-{version}-win-x64.zip

Linux (x64): sapmock-service-{version}-linux-x64.tar.gz

Linux (ARM): sapmock-service-{version}-linux-arm64.tar.gz

macOS (Intel): sapmock-service-{version}-osx-x64.tar.gz

macOS (M1/M2): sapmock-service-{version}-osx-arm64.tar.gz

Linux/macOS

# Extract tar -xzf sapmock-service-{version}-linux-x64.tar.gz cd sapmock-service # Set environment variables export SAPMOCK_LICENSE="your-license-key" export Sap__Host="https://sap.company.com:8000" # Run ./SapMock.Service

Windows

# Extract ZIP file, then: set SAPMOCK_LICENSE=your-license-key set Sap__Host=https://sap.company.com:8000 # Run SapMock.Service.exe

Linux Systemd Service

# Create systemd service file: /etc/systemd/system/sapmock.service [Unit] Description=SapMock OData Service After=network.target [Service] Type=notify WorkingDirectory=/opt/sapmock-service ExecStart=/opt/sapmock-service/SapMock.Service Environment="SAPMOCK_LICENSE=your-license-key" Environment="Sap__Host=https://sap.company.com:8000" Restart=on-failure User=sapmock Group=sapmock [Install] WantedBy=multi-user.target # Enable and start sudo systemctl daemon-reload sudo systemctl enable sapmock sudo systemctl start sapmock

Environment Variables Reference

VariableRequiredDescriptionExample
SAPMOCK_LICENSEYesYour license key from trial or subscriptionSAPMOCK-DEV-...
Sap__HostOptional*SAP server URL (protocol + host + port only)https://sap.company.com:8000
Sap__UsernameNoFallback SAP username (if not using header forwarding)MOCKUSER
Sap__PasswordNoFallback SAP password (if not using header forwarding)••••••••
Sap__ClientNoSAP client number100
Sap__ValidateSslNoValidate SAP server SSL certificatetrue
ASPNETCORE_URLSNoServer listening URLhttp://+:5000

*Note on Sap__Host: Only required if automatically fetching metadata from SAP. If you POST EDMX directly in request bodies, SapMock works completely offline without SAP connection.

Cloud Platform Deployment

SapMock.Service can be deployed to any cloud platform that supports Docker or .NET applications.

Azure

  • • Azure App Service (Web App for Containers)
  • • Azure Container Instances (ACI)
  • • Azure Kubernetes Service (AKS)

AWS

  • • AWS Elastic Container Service (ECS)
  • • AWS Fargate
  • • AWS Elastic Kubernetes Service (EKS)
  • • AWS Elastic Beanstalk

Google Cloud

  • • Google Cloud Run
  • • Google Kubernetes Engine (GKE)
  • • Compute Engine

On-Premises

  • • Docker / Podman
  • • Kubernetes / OpenShift
  • • Linux systemd service
  • • Windows Service (NSSM)

Key principle: SapMock.Service is 100% offline - it never communicates with our servers. Deploy it in air-gapped networks, private VNets, or anywhere you need complete isolation.