Testing File Uploads Without a Real File Server
File uploads are a specific kind of API interaction that a lot of general-purpose mocking advice quietly skips past, because "return this JSON" doesn't obviously apply to "accept this binary file and tell me what happened to it." That gap is exactly why upload handling tends to be under-tested relative to how often it goes wrong in practice — oversized files, wrong content types, and slow uploads on bad connections are all thoroughly normal real-world conditions that a lot of upload code never actually sees before launch.
What's different about testing an upload
A typical JSON API test cares about the request and response bodies. An upload test additionally has to care about:
- The request's encoding — multipart form data, rather than a JSON body, which some HTTP client and testing setups handle less naturally by default.
- Size, since a file upload is the one common case where a request body can be large enough that size limits, timeouts, and partial-transfer behavior actually matter in a way a typical small JSON payload never triggers.
- Content-type validation — does the server (and does your client, before it even sends anything) correctly reject a file whose declared type doesn't match its actual content, or whose extension doesn't match either?
- Progress and cancellation, for anything upload-heavy enough that a user might watch a progress bar or want to cancel partway through.
The failure modes worth deliberately checking
- An oversized file — does the client show a clear "too large" message before even attempting the upload (checked client-side), and does the server reject an oversized upload cleanly rather than accepting a truncated partial file or hanging?
- A slow upload — does a progress indicator actually update, and does whatever timeout exists trigger gracefully rather than silently hanging forever?
- An upload that fails partway through — does the client correctly treat this as a failure and allow retrying, rather than treating a partial transfer as a successful one?
- A malicious or malformed file — a file whose extension says
.jpgbut whose actual bytes are something else entirely, which server-side validation should catch rather than trusting the client-declared content type.
Making these controllable
You don't need a real, persistent file storage backend to exercise most of this — you need an endpoint that accepts an upload and returns a specific, controllable response: success, a size-limit rejection, a slow response (to test progress and timeout handling), or an error partway through. A mocked upload endpoint with a delay configured lets you test the "slow upload, does the progress bar and timeout handling actually work" case on demand, in a fast local test, instead of needing to genuinely upload a large file over a deliberately throttled connection every time you want to check it.
None of this replaces eventually testing against your real storage backend and its real limits. It does mean the client-side handling — the part that decides what a user actually sees while an upload is in flight or after it fails — can be verified quickly and repeatably, instead of only being exercised by whoever happens to be on a slow hotel Wi-Fi connection the day it matters.
Xerat02
Building MockBase.
More from the blog
The Case for Boring APIs: Why Consistency Beats Cleverness
A cleverly-tailored endpoint is often worse for a codebase than a boring, conventional one — because it's the one out of fifty that doesn't behave the way the other forty-nine do.
What Actually Happens When Two Requests Race to Create the Same Thing
A double-click, two open tabs, or a retry that fires while the original is still processing all produce the same scenario: two near-simultaneous requests trying to create one thing. Most "create if it doesn't exist" code gets this wrong.