Skip to content
>_ TrueFileSize.com
TrueFileSize Editorial··7 min read

Test Upload Forms with Generated Files

The upload form worked on my laptop because I tested it with the same tiny PNG every time. Then QA uploaded a 9.8MB CSV, the progress bar jumped from 0 to 100, and the backend returned a validation error nobody surfaced in the UI.

That was not a backend bug. It was a lazy test matrix.

A browser file generator makes upload testing less lazy. You can create small files, custom file size boundaries, wrong formats, and parser fixtures without keeping a pile of throwaway files in the repo.

A better upload form checklist

Start with these cases:

| Case | Generated file | |---|---| | Happy path | 1KB TXT or CSV | | File type validation | JSON accepted, XML rejected, or the reverse | | Size boundary | 999KB, 1MB, 5MB, 10MB | | Parser behavior | CSV with many rows, JSON with nested objects | | Raw byte handling | BIN file at the largest allowed size | | Preview rendering | SVG or HTML if your app previews markup |

If your app accepts large files above 10MB, use Download Tests for bigger CDN-hosted binaries after the browser-generated cases pass.

Manual test flow

  1. Open the Sample File Generator.
  2. Generate a tiny valid file first.
  3. Upload it through your form and check the success state.
  4. Generate a custom file size near your product limit.
  5. Upload unsupported formats and check the error copy.
  6. Repeat with real sample files from collections like CSV, JSON, or PDF when format correctness matters.

The point is not to test every possible byte pattern. The point is to make sure your UI, validation layer, and API agree about what is allowed.

Playwright example

Playwright needs an actual file on disk for setInputFiles. You can still use generated files in two ways: download one manually for regression fixtures, or create test files in the test setup.

import { test, expect } from "@playwright/test";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";

test("shows a useful error for files over the limit", async ({ page }) => {
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "upload-test-"));
  const filePath = path.join(tmp, "too-large.bin");

  // 10MB binary fixture. Use a custom file size here if your app limit differs.
  fs.writeFileSync(filePath, Buffer.alloc(10 * 1024 * 1024, 0x61));

  await page.goto("/upload");
  await page.locator('input[type="file"]').setInputFiles(filePath);
  await expect(page.getByText(/file is too large/i)).toBeVisible();
});

Use the browser generator for quick manual QA. Use generated fixtures in code when you want the test suite to be deterministic.

Test MIME assumptions separately

File extensions are not enough. A .json filename can contain invalid JSON. A .svg file can be text but still fail your sanitizer. A .csv file can be valid but encoded in a way your parser mishandles.

For production validation, compare your expected MIME types in the MIME Type Lookup, then test your server-side validation with real files. Generated files are excellent for workflow coverage; real sample files are still useful for format-specific parsing.

Common mistakes

  • Only testing files under 100KB.
  • Never testing the exact size limit.
  • Treating client-side validation as security.
  • Showing "upload failed" instead of the real reason.
  • Forgetting mobile browsers, where file selection behaves differently.

If you want a quick start, generate TXT, CSV, JSON, XML, BIN, and SVG files at 1KB, 100KB, 1MB, and 10MB. That matrix catches a surprising amount of broken upload UI.