MockLayer vs WireMock for SAP OData Testing
WireMock Is the Right Tool — for REST APIs
WireMock is battle-tested. Over a decade of development, a Docker image, JUnit integration, .NET and Python clients, and a cloud-hosted option. For mocking standard REST and JSON APIs, it's the default choice for good reason.
SAP OData is not a standard REST API.
SAP OData has protocol-level requirements that generic HTTP mocks don't handle — envelopes, date encoding, CSRF tokens, navigation properties. Our CI/CD guide covers each of these in detail. WireMock treats OData as "just HTTP," which means every protocol-specific behavior becomes your responsibility to hand-code in stub files.
This post compares WireMock and MockLayer specifically for SAP OData mocking in backend integration tests. Both tools have different design goals. The question is which one removes more friction when the API you're mocking follows the OData protocol and describes itself via EDMX.
The Core Difference: Stubs vs. Metadata
WireMock works by matching incoming requests to pre-defined stubs. You write a JSON mapping that says "when this URL pattern arrives, return this response body." The response body is a file you author by hand or capture from a real system.
MockLayer works by reading the EDMX metadata that every SAP OData service exposes at its $metadata endpoint. It parses entity types, property definitions, data types, and navigation relationships — then generates structurally correct responses on the fly. No stub files. No response bodies to maintain.
This distinction matters most when the SAP schema changes. With WireMock, every affected stub file needs a manual update. With MockLayer, the next metadata refresh picks up the change automatically.
What a WireMock SAP OData Stub Looks Like
To mock a single entity set with WireMock, you need at minimum two files.
1. A stub mapping (mappings/business-partners.json):
{
"request": {
"method": "GET",
"urlPathPattern": "/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner.*"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"bodyFileName": "business-partners.json"
}
}
2. A response body (__files/business-partners.json):
{
"d": {
"results": [
{
"__metadata": {
"uri": "https://host/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner('1000001')",
"type": "API_BUSINESS_PARTNER.A_BusinessPartnerType"
},
"BusinessPartner": "1000001",
"BusinessPartnerFullName": "Acme Corp",
"BusinessPartnerCategory": "2",
"CreationDate": "/Date(1672531200000)/",
"to_BusinessPartnerAddress": { "__deferred": { "uri": "..." } }
}
]
}
}
That's one entity with six properties. The actual A_BusinessPartnerType in API_BUSINESS_PARTNER has 70 properties. A production stub file for a single entity type runs to hundreds of lines. The service defines 65 entity types total.
A real integration test suite needs stubs for multiple entity sets, multiple $expand paths, a $metadata stub, and CSRF token handling stubs. The $expand problem is combinatorial: to_BusinessPartnerAddress needs one stub, to_BusinessPartnerAddress,to_BusinessPartnerContact needs another, and each expanded path requires its own nested response body with the correct entity type structure.
Nothing in WireMock enforces that your hand-coded response bodies match the EDMX schema. Add a field to the $metadata stub but forget to update a response body — your mock is self-contradictory and your tests pass against data that SAP would never return.
The WireMock Community Has Noticed
The OData support question has been raised directly with the WireMock team. WireMock.Net Issue #556 ("Support OData") requested built-in OData query support — the ability to stub a single response and have WireMock automatically filter results based on OData query parameters. The maintainer closed it as wontfix, stating that "OData is just a specification" and WireMock can stub any request and response. Technically true. Practically, it means every OData query variation is your problem to stub individually.
SAP's own ecosystem documentation confirms the gap. A blog post on the SAP Community about integration testing with the SAP Cloud SDK states plainly that the SDK "does not provide such an easy method to mock the OData call" — so the author resorted to WireMock, hand-coding stub files for each OData endpoint.
Separately, SAP published a mock server example in the Cloud SDK book repository — a custom Node.js server with manually configured EDMX metadata and limited OData V2 support. Its documentation warns it is "not a complete OData service compliant with the OData v2 specification." It exists because nothing off-the-shelf solved the problem.
The Maintenance Scenario: SAP Adds 3 Fields
Initial setup time matters less than maintenance over months and years. Here is a concrete scenario that SAP teams encounter regularly.
SAP releases a support package that adds three new properties to A_BusinessPartnerType: LastChangeDateTime (Edm.DateTimeOffset), ResponsibleCompanyCode (Edm.String, length 4), and BusinessPartnerIsNotReleased (Edm.Boolean).
What changes in WireMock:
Every stub response file that returns A_BusinessPartner entities needs updating. If you have five stubs covering different query patterns (list, single entity, expanded with addresses, expanded with contacts, filtered by category), that is five response body files. In each file, every entity object needs the three new properties added with plausible values — an ISO 8601 timestamp for the DateTimeOffset, a 4-character company code string, and a boolean.
If you also maintain a $metadata stub, the EDMX XML needs the three new <Property> elements added to the A_BusinessPartnerType definition. Miss the metadata stub and your mock serves an EDMX that doesn't describe properties that appear in the data — SAP client libraries that validate responses against metadata will throw.
Miss one response body file and that stub returns entities without the new fields. Your code's null-handling or default-value logic for those fields never gets tested. The bug surfaces in production when the field is present and your deserializer encounters a type it didn't expect.
Total effort: 30-60 minutes of careful, error-prone JSON editing across multiple files. Multiply by the number of SAP services you mock.
What changes in MockLayer:
Re-export the EDMX from SAP (one GET $metadata call) and replace the cached file. Or, if MockLayer has network access to SAP, do nothing — the metadata cache refreshes automatically when the configurable TTL expires (default 24 hours). The three new properties appear in generated responses immediately, with type-correct values: a properly formatted DateTimeOffset, a company code string, and a boolean.
Total effort: zero to five minutes. No file editing.
Adding a new entity set (e.g., A_BusinessPartnerContact):
- WireMock: author a new stub mapping, write a response body with the correct OData envelope and entity structure, look up property names and types in the EDMX
- MockLayer: query it — the entity already exists in the EDMX
Adding $expand to an existing query:
- WireMock: create a new stub for the expanded variant, or update the existing response body with manually nested entities matching the target entity type's schema
- MockLayer: add
$expand=to_BusinessPartnerContactto the URL
Where WireMock Wins
WireMock does things MockLayer architecturally cannot. These are real strengths, not token concessions.
Error simulation. WireMock can return specific HTTP status codes, inject faults, and simulate latency. A fault injection stub for testing retry logic against SAP:
{
"request": {
"method": "GET",
"urlPathPattern": "/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner.*"
},
"response": {
"fault": "CONNECTION_RESET_BY_PEER"
}
}
WireMock supports four fault types: EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK, RANDOM_DATA_THEN_CLOSE, and CONNECTION_RESET_BY_PEER. It also supports fixed delays, random delays (lognormal and uniform distributions), and chunked dribble delays for testing timeout behavior. MockLayer always returns valid mock data for known entities. If your code needs to handle a 503 from SAP or a connection reset during a network timeout, WireMock is the tool.
Request verification. WireMock records every request in an in-memory journal. You can verify that specific endpoints were called with specific parameters, headers, and request bodies:
verify(exactly(1), getRequestedFor(
urlPathEqualTo("/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner"))
.withQueryParam("$top", equalTo("5"))
.withHeader("Accept", containing("application/json")));
This catches bugs where your code sends requests to wrong paths, omits query parameters, or sends incorrect headers. MockLayer does not track request history.
Stateful sequences. WireMock scenarios let you simulate state transitions across requests. A POST-then-GET sequence where the created resource becomes queryable:
{
"scenarioName": "CreateBusinessPartner",
"requiredScenarioState": "Started",
"newScenarioState": "PartnerCreated",
"request": {
"method": "POST",
"urlPath": "/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner"
},
"response": {
"status": 201,
"bodyFileName": "created-partner.json"
}
}
MockLayer is stateless by design. Each request generates fresh data. POST followed by GET will not return the same entity.
Non-OData endpoints. If your application calls SAP REST APIs, function imports, or third-party services alongside standard OData entity sets, WireMock handles all of them. MockLayer only handles SAP OData paths (/sap/opu/odata/ and /sap/opu/odata4/).
Specific response values. If a test needs a specific business partner with exact field values across multiple properties, WireMock gives full control over every byte. MockLayer respects $filter constraints — $filter=BusinessPartnerCategory eq '2' produces entities with that exact value — but you cannot control fields outside the filter expression.
Where MockLayer Wins
MockLayer does things WireMock cannot do without significant custom development:
EDMX-driven generation. Point it at a SAP service and it generates valid responses for every entity type in the service — including entity types you haven't written stubs for. One EDMX file covers all 65 entity types in API_BUSINESS_PARTNER. For the full pipeline setup (GitHub Actions YAML, test code in four languages), see our CI/CD guide.
OData protocol compliance. V2 and V4 envelopes, __metadata objects, /Date(...)/ encoding, $inlinecount — built in, not hand-coded.
Multi-level $expand. $expand=to_BusinessPartnerAddress/to_EmailAddress resolves two levels of navigation properties — entities nested within entities, each with correct structure. In WireMock, this requires deeply nested JSON authored by hand.
Zero-maintenance schema sync. Three-tier caching (memory, disk, SAP source) with configurable TTL. Schema changes propagate without touching test infrastructure.
CSRF token handling. The x-csrf-token: fetch handshake works out of the box.
Filter-aware generation. $filter=BusinessPartnerCategory eq '2' produces entities where that property contains exactly '2'. Supported operators: eq, ne, gt, ge, lt, le, startswith, endswith, contains, and or (picks from provided values). Unconstrained properties are generated from schema with property-name heuristics — fields named *Email* get email addresses, *PostalCode* gets postal codes, *Price* gets currency values.
Comparison
| WireMock | MockLayer | |
|---|---|---|
| Type | General HTTP mock | SAP OData mock |
| EDMX awareness | None | Parses and generates from EDMX |
| Setup per service | Hours to days (manual stubs) | Minutes (one EDMX file) |
| Schema changes | Manual update per stub file | Automatic on metadata refresh |
| OData envelope | Hand-coded | Automatic (V2 and V4) |
| $expand | Separate stub per combination | Multi-level, from EDMX associations |
| CSRF tokens | Manual stub implementation | Built-in |
| Error simulation | Yes (status codes, delays, faults) | No |
| Request verification | Yes (call count, body, params) | No |
| Stateful scenarios | Yes | No (stateless by design) |
| Specific response values | Full control | Schema-correct, filter-aware |
| Non-OData endpoints | Yes | No |
| CI/CD friendly | Yes (Docker, standalone JAR) | Yes (standalone binary) |
| Language support | Any (HTTP) | Any (HTTP) |
When to Use Which
Use WireMock when:
- You need to test error handling and retry logic against SAP failures
- Your tests require specific, deterministic data values across multiple properties
- You're mocking non-OData SAP endpoints alongside OData
- You need to verify that specific requests were sent with correct parameters
- You already have a WireMock infrastructure and only mock 2-3 SAP entity types
Use MockLayer when:
- You integrate with SAP services that have many entity types
- Your tests exercise
$expand,$filter,$top, and other OData query options - Schema changes from SAP support packages are a recurring maintenance burden
- You want CI/CD tests that run without stub file maintenance
- Your team works in .NET, Java, Python, or Node.js (not UI5)
Use both when:
- MockLayer handles the happy-path OData contract testing — "does my code correctly deserialize what SAP sends back?"
- WireMock handles error paths — "does my code survive a 503, a timeout, or a connection reset from SAP?"
- MockLayer covers the 65 entity types in API_BUSINESS_PARTNER with zero stub files; WireMock covers the three error scenarios that need specific fault injection
They serve different purposes. The overlap is the happy-path OData mocking, and that's where the EDMX-driven approach eliminates stub file maintenance.
What About Other SAP Mock Options?
Three tools in the SAP ecosystem address OData mocking. None of them target backend integration testing.
SAP UI5 MockServer (OpenUI5 on GitHub) is a client-side browser mock that intercepts XMLHttpRequest calls via Sinon.js. It reads EDMX and generates OData V2 data — but only inside a running UI5 application. It cannot serve HTTP responses to a .NET, Java, or Python backend. OData V4 support was explicitly declined: OpenUI5 maintainers stated they "will not provide an additional mockserver implementation for OData V4" and closed the feature request in favor of the FE MockServer.
SAP FE MockServer (@sap-ux/fe-mockserver-core) is a Node.js middleware designed for SAP Fiori Elements development. It reads EDMX and serves mock data from JSON files. Its npm documentation states it is "not meant to be consumed directly" — it is intended to run inside the @sap-ux/ui5-middleware-fe-mockserver tooling pipeline, not as a standalone HTTP server for backend tests.
SAP CAP (capire documentation) can mock remote OData services during development. It mocks imported services in-process using its CDS runtime, with data stored in an in-memory database. This requires the full CAP framework, CDS model definitions (not just EDMX), and either a Node.js or Java runtime. CAP's Node.js runtime supports only OData V4 for mocked services. For teams whose backend is not built on CAP, adopting the entire framework for mocking is not practical.
All three tools were built for SAP's own development workflows (UI5, Fiori Elements, CAP). Backend teams integrating with SAP from outside that ecosystem — which is the majority of enterprise integration work — cannot use them.
For a comparison of five mocking approaches including these alternatives, see How to Mock SAP OData Services in Your CI/CD Pipeline. For MockLayer's current limitations (stateless design, no $batch, no business logic validation), that post covers them in the "What This Doesn't Solve" section.
MockLayer is a standalone binary for Windows, Linux, and macOS. If your team uses WireMock today and spends time maintaining SAP OData stubs, a free 5-day trial lets you test the EDMX-driven approach against your own metadata.