intermediate24 min

Chained Requests and Stateful Workflows (Lab)

A hands-on lab: test a realistic multi-step workflow where each request depends on data returned by the one before it.

What you'll learn

  • Design a test that carries data extracted from one response into a later request
  • Identify where a chained workflow can fail even when each individual step works alone
  • Explain why testing steps only in isolation misses real workflow defects

Prerequisites

Explanation

Real usage rarely stops at one request. Create an order, then fetch its id from the response, then use that id to add an item, then use the order's total from that response to apply a payment. Each step depends on data extracted from the step before it — this is a chained request workflow, and it's a genuinely different testing problem from testing any single endpoint in isolation.

Testing each endpoint separately, with hand-picked hardcoded ids, proves each endpoint works when given a plausible id. It does not prove the endpoints work correctly together — that the id returned by "create order" is actually accepted by "add item," in the exact format it's returned in (a number vs. a string masquerading as a number is a classic mismatch here, echoing the integration-level defects from Software Testing Foundations).

A chained-request test carries state explicitly between steps: extract the value you need from each response, and pass it into the next request rather than hardcoding a fixed id. This mirrors real client code, which never knows an order's id in advance — it only learns it from the create response.

Two failure categories are specific to chains and worth testing deliberately: a failure partway through (step 2 succeeds, step 3 fails — what state is the system left in? Is the order now stuck half-created, or does it clean up?), and using stale data from an earlier step after something has changed (fetch an order's total, then apply a discount that changes it, then try to charge the original, now-stale total — does the payment step correctly reject or recompute, or does it silently charge the wrong amount?). Both failure modes are invisible to tests that only ever exercise one endpoint at a time.

Example

A simulated three-step chained workflow: create an order, add an item using the id from step 1, then read the total. No real network calls — deterministic in-memory fixtures.

const db = { orders: {} };

function createOrder() {
  const id = Object.keys(db.orders).length + 1;
  db.orders[id] = { id, items: [], total: 0 };
  return { status: 201, body: db.orders[id] };
}

function addItem(orderId, priceCents) {
  const order = db.orders[orderId];
  if (!order) return { status: 404 };
  order.items.push(priceCents);
  order.total = order.items.reduce((sum, p) => sum + p, 0);
  return { status: 200, body: order };
}

const createResponse = createOrder();
const orderId = createResponse.body.id; // chained: extracted from step 1
const addResponse = addItem(orderId, 500);
console.log(addResponse.body); // { id: 1, items: [500], total: 500 }

Try it yourself

Chain a second addItem call using the same orderId and confirm the total accumulates correctly.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Using the createOrder/addItem functions already defined, chain three calls: create an order, add a 300-cent item, add a 700-cent item, then store the final total in finalTotal (should be 1000).

Checks: correctly chains three calls to reach the right total

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write a function chargeOrder(order, expectedTotal) that simulates a payment step: it should return { status: 409, error: 'stale total' } if expectedTotal does not match order.total (the client's data is stale), or { status: 200, charged: order.total } if it matches.

Checks: a fresh, matching total succeeds · a stale total is correctly rejected, not silently charged

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Common mistakes

  • Testing each endpoint only with hardcoded, hand-picked ids instead of chaining real data extracted from a prior response.
  • Never testing what happens when a chain fails partway through, leaving the system's actual state unverified.
  • Assuming data fetched earlier in a chain is still valid later, without testing what happens when it's gone stale.

Knowledge check

Knowledge check

1. Why is testing endpoints only in isolation insufficient for a multi-step workflow?
2. A payment step is asked to charge an order using a total fetched several steps earlier, which has since changed. What should a well-designed API do?
3. What does 'chaining' mean in the context of chained-request testing?

Takeaway

Chained-request testing carries real data between steps rather than hardcoding ids, and deliberately tests partial-failure and stale-data scenarios that isolated single-endpoint tests can never reach.

Summary

This lab practiced chaining requests by extracting data from one response into the next, and covered the partial-failure and stale-data failure modes specific to multi-step workflows.

References

Your notes

Notes save automatically.

Finished this lesson?

Mark it complete to track your progress and schedule a future review.