A File Upload Form Is Not Done Until You Test the Download
A file picker can look perfect and still fail the person who needs the file. A successful click only proves that the browser accepted an input. It does not prove that the server validated the file, stored the correct by
A file picker can look perfect and still fail the person who needs the file.
A successful click only proves that the browser accepted an input. It does not prove that the server validated the file, stored the correct bytes, linked them to the right record, or returned a usable download later.
For acceptance testing, treat an upload as a five-stage path:
- Input β the browser selects a file.
- Validation β the application decides whether it is allowed.
- Storage β the bytes and metadata are saved.
- Delivery β an authorized user retrieves the same file.
- Recovery β a failed attempt can be repeated safely.
This model turns a vague requirement such as βthe upload worksβ into checks that a developer, tester, and product owner can verify together.
Start with a small test matrix
Do not begin with dozens of random files. Use a compact set where every sample answers a specific question.
| Sample | What it proves |
|---|---|
| Small valid file | The happy path works |
| Valid file near the size limit | The documented limit is real |
| File just over the limit | The server rejects it clearly |
| Disallowed extension | Type rules are enforced |
| Empty file | Zero-byte behavior is intentional |
| Duplicate filename | Existing uploads are not overwritten accidentally |
| Interrupted request | Retry and cleanup behavior are safe |
Record the expected result before running each test. Otherwise a vague message like βUpload failedβ can be mistaken for acceptable behavior even when it gives the user no way to recover.
1. Test the input layer
The file picker should explain the allowed types and maximum size before selection. The browser may filter the picker with the accept attribute, but that is only user-interface guidance. It is not security.
Check at least these details:
- keyboard users can reach and activate the control;
- the selected filename is visible without exposing a local filesystem path;
- replacing a selection does not silently submit the previous file;
- removing the selection returns the form to a predictable state;
- the displayed size and type rules match the server rules.
Drag-and-drop is a second input method, not a separate upload system. It should produce the same validation result as the ordinary picker.
2. Verify validation on the server
Client-side checks improve feedback, but the server must make the final decision. Requests can be sent without the browser interface.
A practical validation sequence is:
receive request
β confirm request size
β inspect extension and declared MIME type
β inspect actual file signature when relevant
β generate a server-side identifier
β store outside executable paths
β return a stable status
Test the boundary values exactly: the documented limit, one byte below it, and one byte above it. Confirm that the error message identifies the rejected rule and does not claim that the file was saved.
If the application renames files, preserve the original name as metadata for the interface while using a safe internal identifier for storage.
3. Confirm storage and record identity
An upload can reach storage but still attach to the wrong order, ticket, user, or article.
After a successful request, verify:
- the database record and stored object refer to the same identifier;
- the correct account owns the file;
- the original filename, size, and type are recorded accurately;
- duplicate names do not overwrite unrelated files;
- a failed database update does not leave an orphaned object indefinitely;
- a failed object write does not create a βsuccessfulβ record.
This is where a transactional status helps. An item can move through explicit states such as pending, stored, available, and failed instead of jumping directly from nothing to βcomplete.β
4. Test the download, not only the upload
The original bytes are the acceptance target. Download the file through the same route a real user will use, then compare it with the source.
For deterministic files, a hash comparison is useful:
sha256sum original.pdf downloaded.pdf
The two hashes should match unless the product intentionally transforms the file. If transformation is expected, define a different comparison: page count, image dimensions, generated preview, or another observable property.
Also verify:
- the download uses the intended filename;
- the response type is correct;
- an unauthorized user cannot retrieve the object by guessing a URL;
- an expired link returns an intentional message;
- cached responses do not return an older file with the same display name;
- mobile and slow-network users get a visible progress or completion state.
A green βuploadedβ badge is not enough evidence if the resulting link is broken.
5. Make failure and retry behavior explicit
Failures are ordinary product states. Test them deliberately.
Interrupt one request, retry it, and inspect the result. The application should not create two records, charge twice, send duplicate notifications, or leave two objects where only one is visible.
A useful retry design answers four questions:
- Can the user tell whether the first attempt completed?
- Is repeating the action idempotent or safely deduplicated?
- Is partial data removed or resumable?
- Does the error message suggest the next action?
βTry againβ is only helpful when a second attempt cannot make the situation worse.
A concise acceptance checklist
Before sign-off, demonstrate the following in one test session:
- one valid upload reaches an available state;
- the downloaded bytes match the source;
- an invalid type is rejected by the server;
- the size boundary behaves exactly as documented;
- the file is linked to the correct record and account;
- unauthorized retrieval is denied;
- interruption and retry do not create duplicates;
- every failure message states what happened and what the user can do next.
This checklist is intentionally small. It covers the path that matters without pretending that clicking a file picker is the same as delivering a file.
For a related practical test plan focused on website acceptance, see Ready4site's file upload testing guide.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.