Search code examples
javaautomated-testskaratekarate-call-single

Handling File Uploads with Dynamic File Names in Karate Test Framework


I’m working on a test in Karate where I need to upload an Excel file to an API. The challenge is that each test run should generate a unique file name to avoid conflicts (e.g., to prevent 409 Conflict errors).

My Scenario: Original Setup:

The original Excel file is located in src/test/resources/testdata/sample.xlsx. I need to generate a unique file name (e.g., sample_<UUID>.xlsx) and copy the original file to a temporary directory with this new name. After copying, I need to upload this newly named file via an API endpoint and then clean up the file.

Problems Encountered: It will create the file in the desired location with the new name, yet it gives me file not found exception most likely at the below line is what I guess And multipart file file = { read: '#(newFilePath)', filename: '#(newFileName)', contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }

Karate Test:

Feature: File Upload Endpoint with Dynamic File Name

Background:
* def UUID = Java.type('java.util.UUID')
* def Files = Java.type('java.nio.file.Files')
* def Paths = Java.type('java.nio.file.Paths')
* def StandardCopyOption = Java.type('java.nio.file.StandardCopyOption')
* def System = Java.type('java.lang.System')

# Function to generate a unique file name
* def generateUniqueFileName =
  """
  function() {
    var uuid = UUID.randomUUID().toString();
    return 'sample_' + uuid + '.xlsx';
  }
  """

# Function to copy file with a new name to a specific directory
* def copyFileWithNewName =
  """
  function(sourcePath, targetDir, newFileName) {
    var sourceFile = Paths.get(sourcePath);
    var targetPath = Paths.get(targetDir, newFileName);
    Files.createDirectories(targetPath.getParent());
    Files.copy(sourceFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
    return targetPath.toString();
  }
  """

# Set up paths
* def projectDir = System.getProperty('user.dir')
* def originalFilePath = projectDir + '/src/test/resources/testdata/sample.xlsx'
* def targetDir = projectDir + '/src/test/resources/testdata/temp'

# Generate a new file name and copy the file
* def newFileName = generateUniqueFileName()
* def newFilePath = copyFileWithNewName(originalFilePath, targetDir, newFileName)
* print 'New file created at:', newFilePath

Scenario: Upload an Excel file
Given url 'http://localhost:8080/upload/file'
And multipart file file = { read: '#(newFilePath)', filename: '#(newFileName)', contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
When method post
Then status 202
And match response == 'File uploaded successfully'

# Clean up: delete the temporary file
* eval Files.deleteIfExists(Paths.get(newFilePath))
* print 'Temporary file deleted:', newFilePath


Solution

  • Firstly - you may not need to copy the file at all. Read the same file and change the filename attribute and it will work as you expect - so try that first.

    As far as I know if read points to a valid file, it should work. So I guess you have to figure this out yourself (or follow this process).

    Also keep in mind that read can be nudged to use absolute paths if needed (use the file: prefix): https://github.com/karatelabs/karate#path-prefixes

    While troubleshooting this, you may be able to use karate.toJavaFile() and print out the name of the file that you pass to the read value.

    You also have the option to use value instead of read and value can be a (Java) byte-array which is in-memory instead of a saved file. One way to create a byte-array is karate.readAsBytes(). Since you are using Java APIs a lot, you should be able to figure out a way to get a byte-array from your excel source.