DEV Community

Cover image for A Developer's Guide to Testing File Upload and Conversion Features
Simon Briggs
Simon Briggs

Posted on

A Developer's Guide to Testing File Upload and Conversion Features

In the middle of the night, I got a Slack ping from our QA lead: "The production upload endpoint is silently dropping 40% of PDFs over 8MB." No error logs. No stack trace. Just... gone. Users clicked upload, saw a spinner, and got nothing back. It took us three hours to find the culprit: a reverse proxy timeout that had nothing to do with our actual upload code, and everything to do with the fact that nobody had ever tested what happens when a file takes longer than 30 seconds to process.

If you've built any feature that touches file uploads or format conversion, you already know this pain lives in a weird blind spot. Unit tests pass. The demo works. Then real users show up with a 200-page scanned PDF, a corrupted DOCX from 2009, or a file named résumé (final)(2).pdf, and everything you thought you'd covered falls apart.

This is a guide to actually testing that stuff, not just the happy path.

Why file handling breaks differently than other features

Most application logic deals with data you control: JSON payloads, form fields, database rows. File upload and conversion features deal with data that comes from outside your system entirely. The file could be malformed, mislabeled, absurdly large, or crafted specifically to break your parser.

That means your test strategy needs to shift from "does the function return the right value" to "does the system survive contact with reality." Those are different disciplines, and treating them the same is how bugs like my 2 AM incident happen.

Start with a matrix, not a checklist

The instinct is to write a list of test cases. Don't. Build a matrix instead, because upload/conversion bugs live at the intersection of variables, not in any single one.

Your axes should include:

  • File size: empty (0 bytes), tiny (1KB), typical (2-5MB), large (50MB+), and whatever your stated max is, plus one byte over it
  • File type validity: correct extension with correct content, correct extension with wrong content (rename a .zip to .pdf and see what happens), no extension, wrong MIME type header
  • Encoding and structure: password-protected PDFs, PDFs with embedded fonts, DOCX files saved by Word vs. Google Docs vs. LibreOffice (they are not identical under the hood)
  • Network conditions: slow upload, connection drop mid-transfer, client cancels the request
  • Concurrency: same user uploading 10 files at once, 500 different users uploading simultaneously A five-by-five matrix sounds like overkill until you realize that "large file + slow network + client cancel" is exactly the combination that leaves orphaned temp files rotting on your server disk. I've seen that exact bug take down a staging environment because nobody cleaned up partial uploads.

Test the file, not just the upload

Here's where people cut corners. It's easy to test that a file arrives. It's harder, and more important, to test that it arrives correctly.

If you're building a conversion pipeline (PDF to Word, image to PDF, whatever), write assertions against the actual output content, not just "the process didn't throw." A quick example using a Node test with a PDF parsing library:

const fs = require('fs');
const pdfParse = require('pdf-parse');

test('converted PDF preserves original text content', async () => {
 const inputText = 'Invoice #4471 — Total Due: $1,240.00';
 const outputBuffer = fs.readFileSync('./test-output.pdf');
 const parsed = await pdfParse(outputBuffer);

 expect(parsed.text).toContain('Invoice #4471');
 expect(parsed.text).toContain('1,240.00');
});
Enter fullscreen mode Exit fullscreen mode

This catches the class of bugs that "did it return 200 OK" never will, like tables that get flattened into unreadable text blobs, or special characters (em dashes, currency symbols, accented letters) that silently turn into garbage during encoding conversion. I've watched a conversion tool turn "café" into "café" because someone assumed UTF-8 everywhere and forgot the source file was Latin-1.

Fuzz your file inputs

If your app accepts user-uploaded files and does anything with them server-side (parsing, rendering, converting), you should be fuzz testing those inputs the same way you'd fuzz test an API endpoint.

Tools like radamsa or even a simple script that flips random bytes in a valid file can surface parser crashes you'd never think to test manually:

# quick and dirty byte-flip fuzzer for local testing
python3 -c "
import random
with open('sample.pdf', 'rb') as f:
   data = bytearray(f.read())
for _ in range(50):
   i = random.randint(0, len(data) - 1)
   data[i] = random.randint(0, 255)
with open('fuzzed.pdf', 'wb') as f:
   f.write(data)
"
Enter fullscreen mode Exit fullscreen mode

Run your conversion pipeline against a batch of these mutated files and watch what happens. A well-built system should fail gracefully with a clear error. A fragile one crashes the worker process, or worse, hangs indefinitely on a malformed byte stream and quietly eats your server's memory.

Automate the boring parts, but keep a manual pass

CI pipelines are great at running the same 200 file combinations every commit. They're bad at catching weird stuff, like a font-rendering issue that only shows up visually, or a table that technically "converts" but looks wrong to a human eye.

I keep a small folder of "problem files" collected over time: real files that broke something in production, sanitized of any sensitive data. Before shipping any change to upload or conversion logic, I run that folder through manually and actually look at the output. It takes ten minutes and has caught things automated tests missed every single time.

Don't forget the format edge cases

If your product does any actual file conversion, whether that's PDF to Word, PDF to Excel, or images to PDF, this is where testing gets genuinely tricky. Tables that shift columns, hyperlinks that break mid-conversion, fonts that substitute themselves with something ugly- these are the bugs users actually notice and complain about, even when your test suite is green.

When I'm sanity-checking a conversion feature quickly, without spinning up a whole test harness, I'll sometimes just run the file through PDF Conveter as a second opinion. It's a free browser-based tool, and comparing its output against mine is a fast way to spot whether a formatting issue is a "PDF is just weird" problem or something actually broken in my own pipeline.

The real lesson

File upload and conversion features are deceptively simple to build and genuinely hard to make robust. The gap between "works in my testing" and "works for every user who throws a real-world file at it" is where most of the painful bugs live.

Build your matrix, write output-content assertions instead of status-code assertions, fuzz your inputs, and keep a folder of real broken files as your manual regression suite. Your 2 AM self will thank you.

Top comments (0)