DEV Community

Jeff
Jeff

Posted on Originally published at powerduck.com

Mocking File Uploads: Returning 413 for an Oversized Payload and 201 on Success

Quick answer

When you mock a file upload endpoint, do not return a single canned success for every request. The real server branches on the payload: a small upload returns 201 with a stored object, an oversized one returns 413 Payload Too Large, and often a wrong content type returns 415. If your mock always answers 201, the client's size-guard, retry, and error-handling code never executes in development, and the first time it runs is in production against a real limit. Build the mock to branch on the same request signals the server uses, and make the size threshold explicit so the client team can test both sides of the boundary deterministically.

What a realistic upload contract looks like

Suppose the upload endpoint accepts a multipart body and enforces a 5 MB limit announced both in the contract and in the error response.

Under the limit the server creates the object and returns its identifier. Over the limit it refuses before storing anything and tells the client what the limit was.

A mock that only ever emits the 201 case hides the entire branch the client needs to render a useful message and prevent the upload from being re-sent pointlessly.

Why a single canned response is dangerous

Uploads are where client error handling is most valuable and least exercised. Real users hit size limits, network interruptions, and virus-scan rejections. Developers in a local environment rarely have a 6 MB test file ready, so they upload a tiny placeholder and see only the success path. If the mock matches that habit, the 413 branch and the code that disables the submit button, shows the limit, and avoids an automatic retry are all unverified until a real user triggers them. The defect is not in the mock's data; it is that the mock removed the conditionality the server depends on.

Request condition Server response Client behavior to exercise
Size under maxBytes, valid type 201 with object id Show success, store the returned id
Size over maxBytes 413 with limit details Block retry, display the limit
Wrong or missing content type 415 Prompt for a supported file
Missing file part 400 Validate before sending

Building a branching mock

In a spec-driven mock, encode the limit once and branch on the request's actual length and content type. A small Node example makes the boundary explicit and easy to test.

With this in place, tests can generate a buffer just under and just over the threshold and assert both branches instead of relying on a fixed fixture. Explicitly test the boundary value itself so off-by-one errors in the comparison surface immediately.

Content-Length is not the file size

One trap when branching on size is treating the request's Content-Length as the exact byte count of the uploaded file. In a multipart message it is not: the header includes the boundary delimiters, each part's headers, and any additional form fields, so the on-wire length is always larger than the file itself. A mock that compares Content-Length directly against the file-size limit will reject a file that is legitimately just under the threshold, and the error only appears for uploads near the boundary. Measure the decoded file part's bytes for the limit decision, and use the request length only as a cheap early rejection for obviously oversized bodies. Modeling that distinction in the mock is also what lets the client team test the narrow case where a valid file produces a Content-Length slightly above the advertised number without being a real violation.

Keeping the mock honest with the contract

The limit should not be a magic number copied into the mock by hand. Put maxBytes in the OpenAPI description or a shared example and have the mock read it, so when the server team raises the limit, the mock changes at the same time. Then add a contract test that sends a payload reported as the limit and confirms the mock and the spec agree on whether it is accepted. This is the same discipline you would apply to any status code: the mock's branch conditions need to be traceable to the real contract, not invented for convenience.

FAQ

Should the client enforce the size limit before uploading?

Yes, as a usability improvement, but not as a substitute for the server check. Client-side validation gives instant feedback and saves bandwidth, while the server remains the authoritative guard because clients can be bypassed. The mock should still return 413 so the server-side branch is tested.

How do I test a 413 without creating huge files?

Generate an in-memory buffer sized relative to the configured limit rather than storing a real large file on disk. Build it as maxBytes minus one, equal to maxBytes, and maxBytes plus one, and assert the response for each. This keeps tests fast and deterministic.

Is a 413 ever retryable?

Usually not for the same unchanged payload, because resending an oversized body will fail again. The error body should say so to prevent an automatic retry loop. A retry only makes sense if the client can change the request, such as compressing or splitting the file.

What about chunked or resumable uploads?

Those need a different mock that models an upload session, per-part acknowledgments, and an abort path. The principle is unchanged: branch on the same signals the server uses, and make every documented response reachable in tests rather than only the success case.

This is part of the The API Mocking Guide — start there for the full approach to building mocks that stay faithful to the real contract.

Top comments (0)