API Testing with Karate: Tips & Tricks
Karate is a lightweight and very flexible testing framework built on top of Cucumber that supports BDD-style testing and also offers great HTTP API testing support.
I’ve been using it in various projects, and I want to share some tips and tricks that have significantly improved my API testing experience.
I’ll keep updating this post as I learn more, but feel free to drop your own tips or questions in the comments section below. Happy testing!
Use karate.jar
Karate has few different runtime options, official documentation is very focused on Maven and Gradle, but I find the easiest way to start is with standalone karate.jar.
Only four steps to start:
Install java
Download karate.jar
Create example my.feature file
Run ‘java -jar karate.jar my.feature’
And that is it.
If you need an example my.feature file, just to get started, use this:
Feature: Testing httpbin
Scenario: Open httpbin
url 'https://www.httpbin.org/anything'
request { foo: 'bar' }
method postKarate.jar in GitHub Actions
GitHub actions runner comes with few java versions preinstalled, however Java 11 is active by default. Karate needs Java 17 so this version needs to be configured first:
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin' # Options: 'adopt', 'zulu', 'temurin', etc.
java-version: '17'
- name: 'Test API with karate'
shell: bash
run: |
java -jar karate.jar my.featureAdding environment variables to karate.jar
If you need to use environment variables (secrets, api-key, dev, staging, production specifics…) in your tests you can provice them to karate.jar with ‘java -D’ option.
java -Dsome.name="$ENV_VAR" -jar karate.jar my.featurefor local environment variable or:
java -Dsome.name=${{ secrets.envvar }} -jar karate.jar my.featurefor GitHub action secrets.
Access variable in karate feature file:
* def somename = karate.properties['some.name']Adding JavaScript functions
Karate allows for JavaScript scripting, but with some constraints, such as limited IDE support and the need to stick to single functions within separate files.
Official documentation advises to use Java functions instead, but if you want to stay with JavaScript here are the options.
Oneliner
You can add simple one line function in .feature files:
* def sayHello = function(){return “Hello!!!”} If you add this function in Background then you can reuse it in all Scenarios, within this feature file.
However, this is usefull for only simple functions, if you have more complex ones, you can separate them in their own files. There are two options, either .js or .feature file.
Separate .js file
You can create your function in separate .js file like this:
function fn(creds) {
var temp = creds.username + ':' + creds.password;
var Base64 = Java.type('java.util.Base64');
var encoded = Base64.getEncoder().encodeToString(temp.toString().getBytes());
return 'Basic ' + encoded;
}This is an example from official documentation for basic authentication. It can be reused in different feature files in all your tests.
It also uses Java calls so you can mix it with JavaScript code.
.js file can contain only one function and its name must be ‘fn’
To call it from your feature file use:
* header Authorization = call read('basic-auth.js') { username: 'username', password: 'password' }Separate .feature file
Next option is to create separate feature file (‘helloname.feature’) that contains JavaScript function. If you have a longer, multiline function you need to enclose it within tripple qoutes (“““…”””). With this you lose editor support for autocorrection.
@ignore
Feature:
Scenario:
* def helloName =
"""
function(name){
return 'Hello' + name;
}
"""@ignore tag is added so that this feature is ignored during standard execution.
You can use this function with following code:
* def util = call read('helloname.feature')
* def hellonamestring = util.helloName('myname') 
