File endpoints (profile pictures, invoices, CSV imports, report exports) carry their own risks: wrong content types, oversized uploads, files that download corrupted, and uploads that can be abused. This guide shows how to test both directions with REST Assured.
Multipart File Upload
// Single file upload
@Test
public void uploadProfileImage_validJpeg_returns200() throws IOException {
File imageFile = new File("src/test/resources/testdata/profile-photo.jpg");
given()
.spec(withAuth("customer"))
.contentType("multipart/form-data")
.multiPart("file", imageFile, "image/jpeg")
.when()
.post("/api/users/42/profile-image")
.then()
.statusCode(200)
.body("imageUrl", notNullValue())
.body("imageUrl", startsWith("https://cdn."))
.body("fileName", equalTo("profile-photo.jpg"))
.body("fileSize", greaterThan(0))
.body("mimeType", equalTo("image/jpeg"));
}
// Upload with additional form fields
@Test
public void uploadDocument_withMetadata_returns201() {
File pdfFile = new File("src/test/resources/testdata/invoice.pdf");
given()
.spec(withAuth("admin"))
.contentType("multipart/form-data")
.multiPart("file", pdfFile, "application/pdf")
.multiPart("title", "Invoice Q1 2025")
.multiPart("category", "FINANCIAL")
.multiPart("description", "Q1 2025 invoice for client ABC")
.when()
.post("/api/documents")
.then()
.statusCode(201)
.body("id", notNullValue())
.body("title", equalTo("Invoice Q1 2025"))
.body("category", equalTo("FINANCIAL"));
}
// Negative: file too large
@Test
public void uploadImage_exceedsMaxSize_returns413() {
File largeFile = TestFileGenerator.createFileOfSize(11 * 1024 * 1024); // 11MB > 10MB limit
given()
.spec(withAuth("customer"))
.contentType("multipart/form-data")
.multiPart("file", largeFile, "image/jpeg")
.when()
.post("/api/users/42/profile-image")
.then()
.statusCode(413) // Payload Too Large
.body("error.code", equalTo("FILE_TOO_LARGE"));
}
// Negative: wrong MIME type (trying to upload exe as image)
@Test
public void uploadImage_wrongMimeType_returns415() {
File maliciousFile = new File("src/test/resources/testdata/malware.exe");
given()
.spec(withAuth("customer"))
.contentType("multipart/form-data")
.multiPart("file", maliciousFile, "image/jpeg") // lying about MIME type
.when()
.post("/api/users/42/profile-image")
.then()
.statusCode(415) // Unsupported Media Type
.body("error.code", equalTo("INVALID_FILE_TYPE"));
}
File Download Testing
// Download file and verify content
@Test
public void downloadInvoice_validId_returnsCorrectFile() throws IOException {
byte[] fileBytes = given()
.spec(withAuth("customer"))
.pathParam("id", "INV-2025-001")
.when()
.get("/api/invoices/{id}/download")
.then()
.statusCode(200)
.header("Content-Type", containsString("application/pdf"))
.header("Content-Disposition", containsString("attachment"))
.header("Content-Disposition", containsString("invoice-INV-2025-001.pdf"))
.extract().asByteArray();
// Verify file is a valid PDF (starts with PDF magic bytes)
assertThat(fileBytes).isNotEmpty();
assertThat(new String(fileBytes, 0, 4)).isEqualTo("%PDF");
// Optionally save and verify file size
assertThat(fileBytes.length).isGreaterThan(1000); // at least 1KB
}
// Verify download URL from upload response actually works
@Test
public void uploadThenDownload_fileIntegrityPreserved() throws IOException {
File original = new File("src/test/resources/testdata/test-document.pdf");
byte[] originalBytes = Files.readAllBytes(original.toPath());
// Step 1: Upload
String downloadUrl = given()
.spec(withAuth("admin"))
.multiPart("file", original, "application/pdf")
.when().post("/api/documents")
.then().statusCode(201)
.extract().jsonPath().getString("downloadUrl");
// Step 2: Download and compare bytes
byte[] downloadedBytes = given()
.spec(withAuth("admin"))
.when().get(downloadUrl)
.then().statusCode(200)
.extract().asByteArray();
assertThat(downloadedBytes).isEqualTo(originalBytes); // byte-for-byte match
}
File API Test Checklist
| Area | Tests |
|---|---|
| Valid uploads | Each allowed type and size; metadata (name, size, content type) returned correctly |
| Validation | Disallowed type (expect 400/415), over the size limit (413), zero-byte file, missing file part |
| Security | File renamed to a safe extension but with dangerous content, path traversal in the file name (../../x), very long names, files served back with the correct content type |
| Download | Status, Content-Type, Content-Disposition file name, size and checksum match the original; unauthorised users can't download |
| Large files | Timeouts, partial uploads, and range requests if the API supports resuming |
Compare a checksum (for example SHA-256) of the downloaded file with the original rather than only checking the size; a corrupted file can have the right length.
FAQs
How do you upload a file with REST Assured?
Use multiPart("file", new File("path"), "image/png"), add other form fields with more multiPart or formParam calls, and post to the endpoint; REST Assured sets the multipart content type.
How do you verify a downloaded file in an API test?
Read the response as bytes (asByteArray()), check status and headers, then compare the size and a checksum with the expected file, or parse its contents if it is CSV or JSON.
Which status code should an oversized upload return?
Usually 413 Payload Too Large; some APIs return 400 with a validation message. Check the API's documented behaviour and assert on that.