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"));
}
Advertisement

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

AreaTests
Valid uploadsEach allowed type and size; metadata (name, size, content type) returned correctly
ValidationDisallowed type (expect 400/415), over the size limit (413), zero-byte file, missing file part
SecurityFile 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
DownloadStatus, Content-Type, Content-Disposition file name, size and checksum match the original; unauthorised users can't download
Large filesTimeouts, 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.