How to Write Test Cases for File Upload (With Examples)

How to Write Test Cases for File Upload (With Examples) starts with understanding the core purpose of the upload feature and the risks it introduces. File upload is a common capability in web and mobi

June 13, 2026 · 16 min read · How-To Guides

How to Write Test Cases for File Upload (With Examples) starts with understanding the core purpose of the upload feature and the risks it introduces. File upload is a common capability in web and mobile applications, yet it is also one of the most error‑prone areas because it touches the filesystem, security controls, and often integrates with backend services such as virus scanners, image processors, or storage buckets. A well‑designed test suite for upload must verify that the feature works correctly for valid inputs, rejects invalid ones gracefully, and behaves predictably under stress or unusual conditions. This guide walks you through the full lifecycle of creating high‑signal test cases: from dissecting the upload workflow, through building a concrete test matrix, to automating the checks and tying them back to requirements. You will find a ready‑to‑use table of 20+ example test cases, practical code snippets for both manual and automated execution, and a short checklist you can bookmark for future reference.

How to Write Test Cases for File Upload (With Examples): Foundations

Understanding upload workflow

Before writing any test case, map the end‑to‑end flow of a file upload in your system. Typical steps include:

  1. User selects a file via a UI control (HTML , native picker, drag‑and‑drop zone, or API endpoint).
  2. Client‑side validation runs (file type, size, name length).
  3. The file is transferred to the server, often via a multipart/form‑data POST request or a direct PUT to object storage.
  4. Server‑side validation occurs (MIME type verification, virus scan, quota check).
  5. The file is stored permanently or temporarily, and a reference (URL, database record) is returned.
  6. The UI updates to reflect success or shows an error message.

Each of these steps is a potential failure point. For example, client‑side validation can be bypassed, multipart boundaries can be malformed, and storage services can return transient errors. By enumerating the flow, you ensure that test cases cover not only the happy path but also the boundaries where the system interacts with external components.

Risk areas

Identify the risk categories that are most relevant to upload:

These risk areas guide the selection of positive, negative, and edge test cases later on.

Test case anatomy

A test case for file upload should contain the following fields:

FieldDescription
IDUnique identifier (e.g., UP‑001) for traceability.
TitleShort, readable summary of what is being validated.
PreconditionsSystem state required before execution (e.g., user logged in, storage quota available).
StepsOrdered actions to perform the test (UI interactions, API calls, data setup).
Test DataSpecific file attributes (name, size, type, content) used in the steps.
Expected ResultObservable outcome (UI message, HTTP status, stored file checksum, log entry).
Post‑conditionsAny cleanup needed (delete uploaded file, reset quota).
PriorityRisk‑based ranking (P0‑P2) that informs execution order.
TagsKeywords for filtering (security, boundary, automation).

Having a consistent template makes it easier to review, maintain, and trace test cases to requirements or user stories.

How to Write Test Cases for File Upload (With Examples): Building the Test Matrix

Positive test cases

Positive tests confirm that the upload works as intended for valid inputs. Start with the most common scenarios and then add variations that exercise different code paths.

IDTitlePreconditionsStepsTest DataExpected Result
UP‑001Upload a valid PDF under size limitUser authenticated, quota > 10 MB1. Open upload dialog 2. Select report.pdf (5 MB, PDF) 3. Click Uploadreport.pdf, 5 MB, application/pdfUpload succeeds, server returns 200 OK with file ID, UI shows success toast
UP‑002Upload an image with EXIF orientationUser authenticated, storage empty1. Drag photo.jpg (2 MB, JPEG) onto drop zone 2. Wait for completionphoto.jpg, 2 MB, image/jpeg, contains EXIF Orientation=6Image stored, orientation preserved, thumbnail generated correctly
UP‑003Upload via API with multipart/form‑dataService token valid, endpoint reachable1. POST to /api/upload with form‑field file containing data.txt (1 KB)data.txt, 1 KB, text/plainResponse 201 Created, JSON includes { "url": "https://storage.example.com/uploads/data.txt" }
UP‑004Upload a file with Unicode filenameUTF‑8 support enabled1. Choose résumé.pdf (3 MB) 2. Submitrésumé.pdf, 3 MB, application/pdfFile stored with original filename, accessible via same Unicode name
UP‑005Upload after network retryUnstable network simulator active1. Initiate upload of big.zip (50 MB) 2. Simulate 30 % packet loss after 10 MB transferred 3. Wait for retrybig.zip, 50 MB, application/zipUpload eventually completes, server logs show retry attempts, final file checksum matches source

These five cases already cover happy‑path validation, different file types, API usage, Unicode handling, and resilience to transient network issues.

Negative test cases

Negative tests verify that the system correctly rejects invalid or dangerous inputs. They should also check that error messages are helpful and do not leak stack traces.

IDTitlePreconditionsStepsTest DataExpected Result
UP‑006Upload executable (.exe) fileUser authenticated1. Select malware.exe (2 MB) 2. Attempt uploadmalware.exe, 2 MB, application/octet-streamUpload rejected, server returns 400 Bad Request with message “File type not allowed”
UP‑007Upload zero‑byte fileUser authenticated1. Choose empty.txt (0 bytes) 2. Uploadempty.txt, 0 bytes, text/plainUpload rejected, validation error “File must contain at least one byte”
UP‑008Exceed maximum file sizeQuota allows 100 MB, limit set at 50 MB1. Attempt to upload huge.mov (75 MB)huge.mov, 75 MB, video/quicktimeUpload stopped client‑side, UI shows “File exceeds maximum size of 50 MB”
UP‑009Upload with missing CSRF tokenUser logged in, CSRF protection enabled1. Craft raw POST to /upload omitting csrf_token field 2. Send filetest.png, 100 KB, image/pngServer returns 403 Forbidden, logs show CSRF validation failure
UP‑010Upload filename with path traversalUser authenticated1. Choose file named ../../etc/passwd (1 KB) 2. Upload../../etc/passwd, 1 KB, text/plainUpload rejected or filename sanitized to etc_passwd, no escape from intended directory
UP‑011Upload file with double extensionExtension‑based validation only1. Choose image.jpg.php (150 KB) 2. Uploadimage.jpg.php, 150 KB, application/x-phpUpload rejected or treated as non‑executable based on final extension
UP‑012Upload with corrupted multipart boundaryNetwork simulator can inject malformed data1. Send POST with deliberately broken boundary ------WebKitFormBoundary missing trailing -- 2. Attach filedummy.dat, 10 KB, application/octet-streamServer returns 400 Bad Request, connection closed gracefully

These cases address file‑type security, size limits, client‑side vs server‑side validation, injection attempts, and malformed protocol data.

Boundary and edge cases

Boundary tests push the limits of accepted values, while edge cases explore uncommon but possible situations that often surface only in production.

IDTitlePreconditionsStepsTest DataExpected Result
UP‑013Upload file exactly at size limitLimit = 10 MB1. Choose limit.bin (10 MB, all zeroes) 2. Uploadlimit.bin, 10 MB, application/octet-streamUpload succeeds, server accepts, no truncation
UP‑014Upload file one byte over limitSame limit1. Choose over.bin (10 MB + 1 B) 2. Uploadover.bin, 10 000 001 bytesUpload rejected, error “File size exceeds limit”
UP‑015Upload many small files rapidlyConcurrency allowed1. Loop 100 times: select tiny_i.txt (1 B) and upload via AJAX without waiting for previous responseEach tiny_i.txt, 1 B, text/plainAll uploads complete, server returns 200 for each, no lost requests
UP‑016Upload while storage quota is exhaustedQuota = 0 B remaining1. Attempt to upload any file (1 KB)quota_test.txt, 1 KB, text/plainUpload rejected, UI shows “Insufficient storage”
UP‑017Upload file with leading/trailing spaces in nameUI trims?1. Choose spaced .txt (note spaces) 2. Upload spaced .txt , 2 KB, text/plainSystem either trims spaces and stores spaced .txt or rejects with “Invalid filename”
UP‑018Upload file with null byte in nameFilename handling1. Provide filename image%00.jpg (URL‑encoded null) 2. Uploadimage%00.jpg, 5 KB, image/jpegUpload rejected or null byte stripped, resulting file named image.jpg
UP‑019Upload during server maintenance modeMaintenance flag enabled1. Try to upload while endpoint returns 503maint.pdf, 3 MB, application/pdfClient receives 503 Service Unavailable, shows retry suggestion
UP‑020Upload file that triggers virus scanner timeoutScanner configured with 30 s timeout1. Upload a large inert file designed to cause scanner to hang (e.g., repetitive patterns)timeout.bin, 200 MB, application/octet-streamUpload either proceeds after scanner timeout with warning, or is rejected with “Scan timeout”

These boundary and edge cases expose off‑by‑one errors, concurrency issues, quota handling, filename sanitization, and interactions with auxiliary services such as antivirus scanners.

Data setup and test data management

Effective test data management prevents flaky tests and ensures reproducibility. Consider the following practices:

Implementing these practices early saves time when the upload feature evolves, because you can regenerate only the affected fixtures rather than revisiting every test case manually.

How to Write Test Cases for File Upload (With Examples): Automation Strategies

Choosing tools

Automation can target the UI, the API, or both, depending on where you want to gain confidence.

When selecting a tool, consider the following criteria:

CriterionPlaywright (Web)Appium (Mobile)REST client (e.g., requests)
Interaction fidelityHigh – handles native file picker via setInputFilesHigh – can send intents to open pickerNone – tests the backend directly
Execution speedModerate (browser launch)Moderate–slow (device/emulator)Very fast (HTTP only)
Setup complexityLow – playwright )Medium (Android SDK, device farm)Low (pip install requests)
Ability to mock servicesCan intercept network with routeRequires proxy or dependency injectionEasy to replace endpoint with mock server
ReportingBuilt‑in HTML trace, videoScreenshots, logsJUnit/XML, custom

Script skeleton examples

Below are minimal, ready‑to‑run snippets that illustrate each approach. Feel free to adapt them to your test framework (pytest, Jest, JUnit, etc.).

#### Playwright (TypeScript) – UI upload


import { test, expect } from '@playwright/test';

test.describe('File upload UI', () => {
  test('uploads a PDF and shows success message', async ({ page }) => {
    await page.goto('/upload-page');
    // Wait for the file input to be attached to the DOM
    const fileInput = page.locator('input[type="file"]');
    await fileInput.setInputFiles('tests/fixtures/report.pdf');
    // Click the upload button (could be a separate element)
    await page.click('button#upload-btn');
    // Expect a toast or inline success message
    await expect(page.locator('.toast-success')).toHaveText(/Upload complete/i);
    // Optional: verify network request
    await expect(page.request().fetch(() => true)).toContain([
      request => request.url().includes('/api/upload') &&
                 request.method() === 'POST' &&
                 request.response()?.status() === 200
    ]);
  });
});

#### Appium (Python) – Android native picker


import unittest
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
import os

class UploadAndroidTest(unittest.TestCase):
    def setUp(self):
        caps = {
            "platformName": "Android",
            "deviceName": "Pixel_4_API_33",
            "app": os.getenv("APK_PATH"),
            "automationName": "UiAutomator2"
        }
        self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)

    def test_upload_image(self):
        # Open screen that launches picker
        self.driver.find_element(MobileBy.ACCESSIBILITY_ID, "btnPickImage").click()
        # Use Android intent to set file directly (bypasses picker UI)
        self.driver.push_file("/sdcard/Pictures/test.jpg",
                              base64.b64encode(open("tests/fixtures/test.jpg","rb").read()))
        # Now select the pushed file via picker (implementation depends on app)
        self.driver.find_element(MobileBy.XPATH,
                                 "//android.widget.CheckedText[@text='test.jpg']").click()
        self.driver.find_element(MobileBy.ACCESSIBILITY_ID, "btnUpload").click()
        # Verify toast
        toast = self.driver.find_element(By.XPATH,
                                         "//android.widget.Toast[contains(@text,'Upload successful')]")
        self.assertTrue(toast.is_displayed())

    def tearDown(self):
        self.driver.quit()

#### Requests (Python) – API‑level upload


import requests
import hashlib

def test_api_upload():
    url = "https://api.example.com/upload"
    files = {"file": ("data.txt", open("tests/fixtures/data.txt", "rb"), "text/plain")}
    headers = {"Authorization": "Bearer " + get_token()}
    resp = requests.post(url, files=files, headers=headers)
    assert resp.status_code == 201
    json_data = resp.json()
    assert "url" in json_data
    # Optional: download and verify checksum
    download = requests.get(json_data["url"])
    assert hashlib.sha256(download.content).hexdigest() == \
           hashlib.sha256(open("tests/fixtures/data.txt","rb").read()).hexdigest()

These snippets illustrate the core actions: locating the file input, attaching a file, triggering the upload, and asserting on the response or UI feedback.

Handling async uploads

Many modern applications offload the actual storage to a background worker after accepting the upload request. In such cases, the immediate HTTP response may only acknowledge receipt, while the file becomes available later. To test this pattern:

  1. Poll for completion – after the POST, repeatedly query a status endpoint (e.g., /upload/{id}/status) until it returns finished or a timeout occurs.
  2. Webhook verification – if the system notifies via a callback URL, expose a temporary test endpoint (using tools like webhook.site or a local ngrok tunnel) and assert that the payload contains the expected file metadata.
  3. Event‑driven assertion – in an automated test, wait for a message on a queue (Kafka, RabbitMQ) that signals successful processing.

Example using polling in Python:


import time

def wait_for_processing(upload_id, timeout=30):
    start = time.time()
    while time.time() - start < timeout:
        status = requests.get(f"https://api.example.com/upload/{upload_id}/status").json()
        if status.get("state") == "finished":
            return True
        time.sleep(2)
    raise TimeoutError("Upload did not finish in time")

Verifying server‑side effects

Beyond confirming that the upload endpoint returned success, validate that the file is correctly persisted:

These assertions turn a simple “HTTP 200” check into a high‑signal verification that the entire upload pipeline functions as intended.

Using SUSA for exploratory upload testing

SUSA (the autonomous QA platform) can complement scripted tests by exploring the upload flow without predefined steps. After you upload an APK or point SUSA at your web URL, it will:

To leverage SUSA for upload testing, simply run:


susatest-agent upload --url https://myapp.com/upload --apk ./myapp.apk --personas curious adversarial power-user

The agent will explore, produce a report, and output ready‑to‑run test scripts that you can add to your CI pipeline. Because SUSA learns from each run, subsequent executions focus on newly discovered paths, increasing coverage over time without manual test‑case authoring.

How to Write Test Cases for File Upload (With Examples): Prioritization, Traceability, and Maintenance

Risk‑based prioritization

Not all test cases carry the same weight. Use a simple risk matrix that combines impact (how severe a failure would be) and likelihood (how probable the defect is). Assign each test case a priority label:

PriorityImpactLikelihoodTypical Examples
P0High (security breach, data loss)Medium‑HighUpload of executable, path traversal, virus‑scan bypass
P1Medium (functional block, user frustration)MediumSize limit enforcement, zero‑byte file rejection, Unicode filename handling
P2Low (cosmetic, rare edge)LowLeading/trailing spaces in filename, network retry logging

Apply this matrix during test‑case creation: label each row in the matrix with its priority. When time is limited, execute all P0 and P1 cases first, reserving P2 for nightly or weekly runs.

Traceability matrix to requirements

Link each test case to one or more requirements or user stories. This traceability helps impact analysis when a requirement changes and demonstrates coverage to auditors.

Test IDRequirement IDRequirement Description
UP‑001REQ‑UPL‑001User shall be able to upload PDF files up to 10 MB
UP‑002REQ‑UPL‑002Uploaded images shall retain EXIF orientation data
UP‑003REQ‑UPL‑003API endpoint /api/upload shall accept multipart/form‑data
UP‑004REQ‑UPL‑004System shall support Unicode characters in filenames
UP‑005REQ‑UPL‑005Upload shall retry automatically on transient network faults
UP‑006REQ‑SEC‑001System shall reject files with executable extensions
UP‑007REQ‑VAL‑002Zero‑length files shall be rejected with a clear message
UP‑008REQ‑LIM‑001Files larger than the configured maximum shall be blocked
UP‑009REQ‑SEC‑002Missing CSRF token shall result in 403 Forbidden
UP‑010REQ‑SEC‑003Filename containing directory traversal sequences shall be neutralized

Maintain this table in a spreadsheet or a test‑management tool (TestRail, Zephyr, etc.). When a requirement is edited, you can quickly locate the affected test IDs and update them accordingly.

Maintaining test cases as the API evolves

File‑upload APIs often change: new parameters are added, storage backends swap, or validation rules tighten. To keep your test suite reliable:

  1. Parameterize endpoints and credentials – store base URLs, API keys, and storage bucket names in environment variables or a config file (test-config.yaml).
  2. Use data‑driven tests – feed the matrix of file attributes from an external CSV/JSON; when a new file type is added, simply append a row.
  3. Adopt contract testing – if you expose a public upload API, generate an OpenAPI/Swagger contract and run tools like Dredd or Pact to verify that your implementation adheres to the contract.
  4. Tag obsolete cases – when a validation rule is removed (e.g., you now allow .exe uploads for a specific admin flow), mark the corresponding test as disabled or move it to a legacy suite rather than deleting it outright; this preserves historical knowledge.
  5. Leverage SUSA’s cross‑session learning – after each run, SUSA remembers which screens and dead ends it has explored. When you add a new upload step (e.g., a mandatory CAPTCHA before the file selector), SUSA will automatically adapt its exploration and surface new test scenarios that you can then convert into scripted cases.

By treating test cases as living artifacts that evolve alongside the product, you avoid the accumulation of stale or duplicated checks.

Common Pitfalls and Production‑Only Issues

Virus scanning delays

Many production environments integrate an antivirus scanner that may take seconds or even minutes to finish, especially for large archives. If your test assumes an instantaneous response, you will observe false failures. Mitigation strategies:

Storage quota and cleanup

Upload tests can quickly consume disk space if they do not delete artifacts. In shared CI agents, this leads to “no space left on device” errors that are unrelated to the code under test. Ensure each test:

Filename encoding and Unicode

Browsers may percent‑encode Unicode characters differently, and some backend frameworks decode them incorrectly, leading to mismatched filenames or 404 errors when retrieving the file. Test with the original name. Include test cases that:

Concurrent uploads and race conditions

When multiple users uploads and race conditions

High concurrency can expose bugs such as:

To detect these, run a stress test that fires off dozens of upload requests simultaneously (using tools like k6, Locust, or a simple for loop with `

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free