Intermediate Chapter 14 · 9 min read

File Upload & Multipart Requests

Upload files via multipart/form-data, handle binary data, test file validation, and work with mixed payloads (files + JSON).

File Upload & Multipart Requests

Many APIs accept file uploads — profile pictures, documents, CSV imports. These use multipart/form-data encoding instead of JSON. Understanding how to construct and test these requests is essential.

Multipart Form Data

Unlike JSON, multipart requests can contain both files and text fields in a single request. Each part has its own content type, and boundaries separate the parts.

What to Test

  • Valid file upload with correct type/size
  • Invalid file types (e.g., uploading .exe when only images allowed)
  • File size limits (too large, zero bytes)
  • Multiple file uploads
  • Mixed payload: file + metadata fields
file-upload.test.js
// File upload using FormData with Fetch
const formData = new FormData();

// Create a test file from a Blob
const fileContent = new Blob(['Hello, this is test content'], {
  type: 'text/plain'
});
formData.append('file', fileContent, 'test-file.txt');
formData.append('description', 'Test file upload');
formData.append('category', 'documents');

const response = await fetch('https://httpbin.org/post', {
  method: 'POST',
  body: formData
  // Note: Don't set Content-Type — Fetch sets it with boundary automatically
});

const result = await response.json();
console.assert(response.status === 200);
console.log('Uploaded files:', Object.keys(result.files));
console.log('Form fields:', result.form);

// Multiple files
const multiForm = new FormData();
multiForm.append('file1', new Blob(['File 1 content']), 'file1.txt');
multiForm.append('file2', new Blob(['File 2 content']), 'file2.txt');

const multiResp = await fetch('https://httpbin.org/post', {
  method: 'POST',
  body: multiForm
});
const multiResult = await multiResp.json();
console.assert(Object.keys(multiResult.files).length === 2);

// Binary upload (e.g., image as raw bytes)
const binaryData = new Uint8Array([137, 80, 78, 71]); // PNG header
const binaryResp = await fetch('https://httpbin.org/post', {
  method: 'POST',
  headers: { 'Content-Type': 'application/octet-stream' },
  body: binaryData
});
console.assert(binaryResp.status === 200);

console.log('File upload tests passed!');
FileUploadTest.java
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import org.testng.annotations.Test;
import java.io.File;

public class FileUploadTest {

    @Test
    public void testSingleFileUpload() {
        File testFile = new File("src/test/resources/test-file.txt");

        given()
            .multiPart("file", testFile, "text/plain")
            .formParam("description", "Test file upload")
            .formParam("category", "documents")
        .when()
            .post("https://httpbin.org/post")
        .then()
            .statusCode(200)
            .body("files", not(anEmptyMap()))
            .body("form.description", equalTo("Test file upload"));
    }

    @Test
    public void testMultipleFileUpload() {
        given()
            .multiPart("file1", new File("src/test/resources/file1.txt"))
            .multiPart("file2", new File("src/test/resources/file2.txt"))
        .when()
            .post("https://httpbin.org/post")
        .then()
            .statusCode(200)
            .body("files.size()", equalTo(2));
    }

    @Test
    public void testFileUploadWithJson() {
        // Mixed: file + JSON metadata
        given()
            .multiPart("file", new File("src/test/resources/test-file.txt"))
            .multiPart("metadata", "{\"type\": \"report\"}",
                       "application/json")
        .when()
            .post("https://httpbin.org/post")
        .then()
            .statusCode(200);
    }

    @Test
    public void testUploadByteArray() {
        byte[] content = "Dynamic content".getBytes();

        given()
            .multiPart("file", "dynamic.txt", content, "text/plain")
        .when()
            .post("https://httpbin.org/post")
        .then()
            .statusCode(200);
    }
}
test_file_upload.py
import requests
import io

def test_single_file_upload():
    """Upload a single file"""
    files = {
        'file': ('test-file.txt', b'Hello, this is test content', 'text/plain')
    }
    data = {
        'description': 'Test file upload',
        'category': 'documents'
    }

    response = requests.post(
        'https://httpbin.org/post',
        files=files,
        data=data
    )

    assert response.status_code == 200
    result = response.json()
    assert 'file' in result['files']
    assert result['form']['description'] == 'Test file upload'

def test_multiple_files():
    """Upload multiple files at once"""
    files = [
        ('files', ('file1.txt', b'File 1 content', 'text/plain')),
        ('files', ('file2.txt', b'File 2 content', 'text/plain'))
    ]

    response = requests.post('https://httpbin.org/post', files=files)
    assert response.status_code == 200

def test_file_from_disk():
    """Upload a file from disk"""
    # Create a temp file
    import tempfile, os
    with tempfile.NamedTemporaryFile(mode='w', suffix='.txt',
                                      delete=False) as f:
        f.write('Content from disk')
        temp_path = f.name

    try:
        with open(temp_path, 'rb') as f:
            response = requests.post(
                'https://httpbin.org/post',
                files={'file': f}
            )
        assert response.status_code == 200
    finally:
        os.unlink(temp_path)

def test_binary_upload():
    """Upload raw binary data"""
    binary_data = bytes([137, 80, 78, 71])  # PNG header bytes

    response = requests.post(
        'https://httpbin.org/post',
        data=binary_data,
        headers={'Content-Type': 'application/octet-stream'}
    )
    assert response.status_code == 200

API Testing Intermediate File Upload & Multipart Requests

Written by PV

© 2026 All Rights Reserved