Q21 of 40 · Karate

How do you handle file uploads in Karate?

KarateMidkaratefile-uploadmultipartapi-testing

Short answer

Short answer: Use the multipart keyword: * multipart file = { read: 'classpath:files/test.csv', filename: 'data.csv', contentType: 'text/csv' } followed by * multipart field = { description: 'Monthly data' } for other form fields. Karate sets Content-Type: multipart/form-data automatically. Then When method POST triggers the upload.

Detail

Karate's multipart keyword configures individual parts of a multipart/form-data request:

# File part — read from classpath
* multipart file = { read: 'classpath:data/users.csv', filename: 'users.csv', contentType: 'text/csv' }

# Additional form fields
* multipart field = { description: 'User import', batchSize: '100' }

# Send the request
Given path '/imports'
When  method POST
Then  status 202
And   match response.jobId == '#string'

Multipart properties:

  • read — file path on classpath (or absolute path); Karate reads the bytes
  • filename — the filename sent in the Content-Disposition header
  • contentType — MIME type of the file part; defaults to application/octet-stream if omitted
  • value — use instead of read to send a string value as a file part

Do not set Content-Type manually — Karate sets it automatically with the boundary parameter.

Asserting the upload result: the POST response typically returns a job ID (async processing) or a count (sync). Use Awaitility / polling for async imports.

// EXAMPLE

file-upload.feature

Feature: CSV file upload

  Background:
    * url karate.properties['api.base.url']
    * header Authorization = 'Bearer ' + bearerToken

  Scenario: Upload user CSV returns a job ID
    * multipart file  = { read: 'classpath:data/100-users.csv', filename: 'users.csv', contentType: 'text/csv' }
    * multipart field = { description: 'Bulk import test', notify: 'false' }
    Given path '/imports/users'
    When  method POST
    Then  status 202
    And   match response.jobId   == '#string'
    And   match response.status  == 'QUEUED'
    And   match response.fileSize == '#number'

  Scenario: Upload oversized file returns 413
    * multipart file = { read: 'classpath:data/oversized.csv', filename: 'big.csv', contentType: 'text/csv' }
    Given path '/imports/users'
    When  method POST
    Then  status 413
    And   match response.error contains 'file size'

// WHAT INTERVIEWERS LOOK FOR

Correct multipart keyword syntax, the read vs value distinction, knowing not to set Content-Type manually, and testing error cases (oversized file, wrong content type) in addition to the happy path.

// COMMON PITFALL

Setting * header Content-Type = 'multipart/form-data' manually — this overwrites the auto-generated boundary, causing the server to fail parsing the request body with a 400 error.