← All posts
March 16, 2026·9 min read

How to mock an inventory API that actually runs out of stock

Static mock servers always return the same response. Real inventory APIs don't. Here's how Mockra's stateful mocks let you simulate stock depletion across sequential requests — including under load test traffic.

Most mock servers have a blindspot: they can't change their mind.

Send GET /products/42 a thousand times and you'll get the same 200 OK with "in_stock": true every single time. That's fine if you're testing the happy path. It's useless if you're testing what happens when stock runs out.

Real inventory APIs behave differently. The first 50 requests for a limited item succeed. The 51st gets a 409 Conflict with "error": "out_of_stock". Your application has to handle that. Your load test should hit it.

This post walks through exactly how to set up a stateful inventory mock in Mockra — one that depletes stock across sequential requests, even under concurrent load test traffic.


Why static mocks fail for inventory testing

The problem isn't just that static mocks are limited. It's that they give you false confidence.

You run a load test against a static mock that always returns 200 OK. Your application looks great. You ship to production. Real users hit the endpoint during a flash sale, stock runs out, and now your cart service is throwing unhandled exceptions because nobody tested the out-of-stock path at load.

There are three patterns teams use to work around this with static mock tools:

Pattern 1: Have the test client send different headers. Postman lets you send an x-mock-response-name header to select which saved example to return. So you manually change the header mid-test. This puts state management in the test script, not the server. It breaks the moment you have concurrent VUs — you can't coordinate state across 500 simultaneous k6 workers.

Pattern 2: Spin up two separate endpoints. One for the in-stock response, one for the out-of-stock response. Route requests between them in your test script. Same problem — the load test has to manage state externally, and it doesn't reflect how the real API behaves.

Pattern 3: Don't test it at all. This is what most teams actually do. The out-of-stock scenario gets tested manually, once, by a developer clicking through the UI. Never under load.

None of these are good options.


How Mockra handles it

Mockra's stateful mocks track request count server-side, per endpoint, and advance through a configured state sequence automatically. The mock decides what to return next — not the test client.

The model is simple:

  • You define an ordered list of steps, each with a status code, response body, and a repeat count
  • The mock serves step 1 for the first N requests, then automatically advances to step 2, then step 3, and so on
  • The last step is sticky — it repeats forever once reached

For an inventory API:

Step 1: 200 OK, in_stock: true  (repeat: 50)   ← first 50 requests succeed
Step 2: 409 Conflict, out_of_stock: true        ← all subsequent requests fail

Request 1 through 50 hit step 1 and get a success response. Request 51 advances to step 2 and gets the out-of-stock error. Requests 52 through 1000 keep hitting step 2 because it's sticky.

State is tracked server-side per namespace, and mutations are atomic. Two concurrent k6 workers can't both "claim" the last unit of stock at the same moment. The counter is consistent under concurrency.


Setting it up in the dashboard

Step 1: Upload your OpenAPI spec

If you have an OpenAPI spec for the inventory API, upload it from the workspace detail page. Mockra will parse every endpoint and auto-generate mock configs with default responses. If you're working from scratch, you can define the endpoints manually.

For this walkthrough, assume you have a GET /products/{id} endpoint with a mock config already created.

Step 2: Open the endpoint and enable stateful mode

In your schema's endpoint list, expand the GET /products/{id} row. Below the standard response editor (status code, body, headers), there's a Stateful Responses section with an enable toggle. Turn it on.

Once enabled, the state sequence editor appears.

Step 3: Define your state sequence

The editor starts with a single step. Set it up as the in-stock response:

Step 1

  • Status code: 200
  • Repeat: 50
  • Body:
{
  "id": "42",
  "name": "Limited Edition Widget",
  "in_stock": true,
  "stock_count": 50,
  "price_cents": 4999
}

Click Add Step to add the out-of-stock step:

Step 2 (last step — automatically sticky)

  • Status code: 409
  • Body:
{
  "error": "out_of_stock",
  "message": "This item is no longer available",
  "id": "42"
}

The "STICKY" label appears on the last step — you don't need to configure this. Once step 2 is reached, it stays there.

Click Save State Sequence. Your mock endpoint is live immediately.

Step 4: Test it

Your mock URL is https://mock.mockra.io/your-namespace/products/42.

# First 50 requests — should return 200
for i in $(seq 1 50); do
  curl -s -o /dev/null -w "%{http_code}\n" \
    https://mock.mockra.io/your-namespace/products/42
done

# Request 51+ — should return 409
curl https://mock.mockra.io/your-namespace/products/42

You'll see 50 lines of 200, then 409 from the 51st request onward.

Step 5: Reset state between test runs

After a test run, the mock is sitting at step 2 (out-of-stock). Before your next run, reset it to step 1.

In the dashboard, click Reset State on the endpoint row. Or via the API:

curl -X POST https://api.mockra.io/mock-configs/{id}/reset-state \
  -H "Authorization: Bearer mra_your_api_key"

For CI/CD pipelines where you're spinning up ephemeral namespaces per test run, the namespace is fresh each time — state starts at step 1 automatically. No reset needed.


A more realistic scenario: flash sale with graduated degradation

Real inventory APIs rarely flip from fully available to completely out of stock in one step. More often there's a middle state — low stock warnings, backorder status, or a queued response.

Here's a four-step sequence that simulates a flash sale more accurately:

Step 1 — Full availability (repeat: 100)

{
  "id": "42",
  "in_stock": true,
  "stock_count": 150,
  "backorder_available": false
}

Step 2 — Low stock warning (repeat: 50)

{
  "id": "42",
  "in_stock": true,
  "stock_count": 12,
  "backorder_available": false,
  "warning": "low_stock"
}

Step 3 — Backorder only (repeat: 30)

{
  "id": "42",
  "in_stock": false,
  "stock_count": 0,
  "backorder_available": true,
  "estimated_restock": "2026-04-01"
}

Step 4 — Fully exhausted (sticky)

{
  "id": "42",
  "in_stock": false,
  "stock_count": 0,
  "backorder_available": false,
  "error": "out_of_stock"
}

With this sequence, your application gets tested against all four states: the happy path, the low-stock UI warning, the backorder flow, and the hard out-of-stock error. A load test that ramps up through these states exercises real code paths that a static mock will never reach.


Running this under load with k6

The state machine handles concurrent requests correctly. Here's a minimal k6 script that demonstrates state depletion across multiple VUs:

import http from 'k6/http';
import { check } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 50 },   // ramp up to 50 VUs
    { duration: '2m', target: 50 },    // hold
    { duration: '10s', target: 0 },    // ramp down
  ],
  thresholds: {
    // We expect some 409s — that's the feature, not a bug
    'http_req_failed': ['rate<0.60'],
  },
};

export default function () {
  const res = http.get('https://mock.mockra.io/your-namespace/products/42');

  check(res, {
    'status is 200 or 409': (r) => r.status === 200 || r.status === 409,
  });
}

A few things to note about running this:

The 409s are expected. Your threshold should account for them. The check verifies your application receives the correct status codes, not that all requests succeed. If your application is silently swallowing 409s and treating them as 200s, this test will catch it.

State advances globally, not per-VU. All 50 VUs are hitting the same endpoint, sharing the same counter. After 50 total requests (across all VUs), the sequence advances to step 2. This is correct — it mirrors how a real inventory API works. Stock units are global, not per-connection.

No coordination needed in k6. Mockra handles concurrency for you. You don't need to synchronize VUs in your test script or worry about race conditions on the step counter.


What this test is actually validating

When you run this test, you're checking several things simultaneously:

  1. Your application handles 409 responses without crashing. If you see 500s from your application after step 2 is reached, you have an unhandled exception.

  2. Your UI degrades gracefully under out-of-stock conditions. The "add to cart" button should disable or show an appropriate message — not silently fail.

  3. Your cart service doesn't accept items that are out of stock. If the frontend is caching the in_stock: true response and the backend is also caching it, you might successfully add an out-of-stock item to a cart. The mock makes this scenario testable.

  4. Your error tracking catches these cases. If you're using Sentry or Datadog, the 409s from your application (not the mock) should appear in your error dashboard after this test. If they don't, your error instrumentation has a gap.

None of this is testable with a static mock. Every one of these failure modes requires the server to change its response based on prior state.


Try it

If you have an inventory or e-commerce API you need to load test, upload your OpenAPI spec and configure a stateful sequence. The free tier includes stateful mocks — you don't need a paid plan to test this.

If you want to test Mockra before signing up, error.mockra.io is a free endpoint that returns random realistic error responses (401s, 429s, 500s, 503s) with no account required. It's useful for testing your application's error handling paths, though it doesn't demonstrate the stateful sequencing described here.

The same stateful configuration works for OAuth flow simulation and payment API state tracking — not just inventory. Those follow-up posts are coming. The inventory scenario is the clearest one to start with because the real-world behavior is intuitive — things run out of stock — and the gap between what static mocks give you and what you actually need is obvious.

Try Mockra free

5,000 requests/month, no credit card required. Upload an OpenAPI spec and get mock endpoints in under 60 seconds.

Start for freeTry error.mockra.io