Karate uses a simple, readable syntax known as Gherkin. You may know Gherkin from Behavior-Driven Development (BDD). It uses the structure of Given, When, and Then:
- Given: This sets up the test.
- When: These are the actions you perform.
- Then: This is where you check the results and run assertions.
Karate is unique because it removes the need for “glue code.” In other BDD frameworks, you need separate code to connect the English-language steps to the programming code. Karate has all the necessary code built into the framework.
Key Features of the Framework
The power of Karate comes from its fundamental design choices:
- Built-in Parallel Runs: If you run many tests, parallel execution is crucial. This feature is included in the basic framework; you do not need to add it later.
- Java and JavaScript Interoperability: You can extend the framework’s abilities by using Java or JavaScript code.
- JSON Focus: Karate handles data easily because everything is treated as JSON. If you work with other formats, like XML, Karate converts it internally to JSON so you can use the same assertion methods across all data types.
Running a Simple API Test
API tests are Karate’s core strength. A typical test scenario uses a feature file, which holds several test scenarios related to the same topic.
To run a test, you specify the URL endpoint using the url keyword. You can use a simple star (*) instead of the traditional Gherkin keywords (Given, When, etc.) to keep scenarios concise.
After setting the URL, you specify the HTTP method:
method get: To retrieve data.method post: To send data.method put: To update data.
Karate automatically stores the API response in a response variable and the status code in response status. You use the match keyword to assert that the status code is correct (for example, matching that the status is 200 for a successful request). This approach makes API test creation very straightforward and readable.
Configuring and Reusing Data
For complex testing, you often need to define settings that apply to multiple tests, such as a base URL for an application.
Using the Configuration File
In a standard Karate project, you use a file called karate-config.js. This file returns a JavaScript object that acts as your configuration.
For instance, you can define a base URL for your application:
// Example of what the config might hold
{
baseUrl: 'http://your-app-url.com'
}
Any configuration parameter you define here becomes automatically available in all your test scenarios. This allows you to use variables like baseUrl directly in your feature files, ensuring consistency.
Capturing and Manipulating API Data
When you run an API test, the response data is available in the response variable. You can easily save this data into a custom variable for later use:
* def products = response
This saves the full response data into a variable named products.
Karate is built on JSON Path operations, allowing you to extract specific pieces of information. For example, if you have a list of products, you can easily pull out an array containing only the product names:
* def onlyTheNames = get products[*].name
This operation takes the products list, selects the name property from every element, and saves the results as an array of names. This ability to manipulate data is critical when you need to combine API data validation with UI testing.
Going Further with UI Testing
Karate is not limited to API calls; it can run UI-based tests in real browsers. This is similar to using tools like Playwright or Selenium. UI tests can either check that different visual components are working correctly, or they can test the data flow through the user interface (which is key for full end-to-end tests).
How UI Testing Works in Karate
To start a UI test, you first configure a web driver. Chrome is often used as the default driver for its flexibility.
You can specify the URL to open the website using the driver keyword:
* driver baseUrl
Once the browser is open, you can target web elements using various locators, including:
- CSS selectors
- IDs
- Link text
- XPath
- Custom attributes (like
data-test-id)
For better readability and maintenance, you can save complex locators into variables:
* def checkoutButton = locate('#data-test-checkout')
Performing UI Actions and Assertions
With the element located, you can perform actions like clicking:
* click checkoutButton
Or you can click directly on a locator:
* click '#data-test-id-cappuccino'
Karate’s power lies in matching and assertions, even on UI elements. You can check the text displayed on a button:
* match checkoutButton.text == 'Total: $19'
This checks the text property of the located element, ensuring that the displayed price is correct.
You can also include wait functions to ensure elements are visible before interacting with them:
* wait for '.modal-content'
Finally, you can take a screenshot at any point in the test. This is useful for visual verification or for automatically capturing the state of the application when a test fails.
Combining API and UI Tests for End-to-End Coverage
The most powerful feature for comprehensive testing is the ability to mix API calls and UI actions in a single scenario. This lets you confirm that the data pulled from the backend API is correctly displayed on the front end.
Handling Dynamic Data and Filters
Sometimes, the data displayed on the front end is modified or filtered from the raw API response. When testing, you must account for these differences to maintain deterministic test results.
In the case of testing a product list, if an API might send back discounted products, but you only want to test the regular product listings displayed on the site, you need to filter the API data first.
Here is where Java and JavaScript interoperability shines. You can define a function inside your test file to handle filtering:
// Defines a function to filter out items marked as 'discounted'
* def filteredProductNames = function(products) {
return products.filter(function(product) {
return !product.name.startsWith('discounted');
}).map(function(product) {
return product.name;
});
}
By filtering the API products first, you know exactly what names should appear on the website.
Extracting Web Element Data
After filtering the API data, you open the browser and locate all the necessary UI elements (like all the product headlines on a page). You locate all elements at once:
* def productsWebElements = locateAll('h4')
Then, you need to extract the visible text from those web elements to compare it with the filtered API data. You can loop through the elements and use the karate.appendTo function to build an array of names shown on the screen:
// Create an empty array
* def productNamesWeb = []
// Loop through each web element
* for (var i = 0; i < productsWebElements.length; i++) {
* karate.appendTo(productNamesWeb, productsWebElements[i].text.trim())
}
The key step is the final assertion. You match the array of names extracted from the website (productNamesWeb) against the array of filtered names from the API (filteredProductNames):
* match productNamesWeb == filteredProductNames
If the lists match exactly, your end-to-end data flow is working correctly.
Mocking APIs for Deterministic Testing
Testing against live APIs that frequently change or contain unpredictable data can lead to unreliable tests. Mocking solves this problem by allowing you to define your own API responses. When your test hits an endpoint, it receives data that you created, not the real data.
Defining a Mock Response
Karate has built-in mocking capabilities that use the same Gherkin syntax.
- Define the Mock Feature: You create a feature file defining your custom response data (e.g., a simplified JSON with only two specific products). You use the
@ignoretag at the top so Karate knows this file is a mock definition, not a test to execute. - Define Conditions (Scenarios): In the mock feature, a “scenario” acts as a filter. You define when the mock should be used. For example, if the path is
/list.jsonand the method isget, then use your custom data. - Define the Response: You use the
responsevariable inside the mock scenario to specify the custom data to send back.
Using the Built-in Mock Server
Karate includes a mock server that runs locally:
- Start the Server: Use the
karate.startmethod, pointing it to your mock feature file. This returns a specific port number where the mock server is running. - Point the Application: You must point your application (or your test’s API calls) to
localhostand the port number provided by the mock server.
This setup lets you test the mocked API itself and verify that the defined conditions and fallback responses work correctly.
Mocked API and UI Testing
The real power of mocking comes when you combine it with UI testing. When you test a real front-end application against a mock, you guarantee that the data source is predictable. This is essential for deterministic testing.
To use a mock with a real browser:
- Start Chrome: Open the browser driver with a blank page.
- Intercept Requests: Use the
driver interceptfunction. You define a pattern (like requesting any JSON file) and instruct the driver to use your mock feature file instead of the real API endpoint.
* driver intercept 'pattern-for-json' 'path-to-mock-feature'
When the front-end application tries to load data, the driver intercepts the request and serves your custom mock data. You can then run complex UI tests, confirming that the front end displays the exact data you defined in your mock, even though the application is running live.
Visual Testing and Screenshot Comparison
Visual testing focuses on comparing screenshots rather than checking individual web element properties. This is often faster for large data tables or complex layouts, as it detects pixel shifts or layout problems.
Visual testing involves comparing a current screenshot against a baseline image. The baseline is an image saved beforehand, showing how the component or page should look.
How to Implement Visual Testing
- Define Viewport: Open the browser and set specific dimensions (width and height) to ensure the screenshots are comparable every time.
- Take a Screenshot: Capture the current state and save it to a variable.
- Compare Images: Use the
compare imagefunctionality. You specify the path to your baseline image and the variable holding the current screenshot.
If the images match, the test passes. If there are pixel differences (even a fraction of a percent), the test fails, and the report shows an overlay highlighting the areas that changed. This helps you quickly spot unintended layout changes or styling regressions.
Performance Testing with Minimal Code
Every test you write in Karate can also be used as a performance test. Karate includes a bridge to the powerful performance testing tool, Gatling.
Setting Up the Gatling Bridge
To run a performance simulation, you only need minimal additional configuration:
- Add Dependencies: If you use Maven, you include the
Karate Gatlingdependency and theGatling Maven pluginin your project setup. - Create a Simulation Class: You write a small Java class that extends the Gatling simulation class. This class tells Gatling which Karate feature file to run.
- Define Load: Inside the simulation class, you set the parameters, such as running 100 simulated users over a period of 10 seconds.
Once configured, you execute the test using a specific Maven command that triggers the Gatling profile. This runs your existing, simple Karate API test multiple times under load.
Analyzing Results
Gatling generates a detailed performance report. This report shows key metrics, including:
- The number of requests executed.
- The distribution of those requests over time.
- The percentage of successful responses (status 200).
This quickly shows you how much load your API or application can handle before errors start appearing.
Conclusion
Karate is far more powerful than just an API testing tool. It provides a simple, unified framework for many types of testing that typically require separate tools.
The features we explored show that you can accomplish complex automation tasks with a minimal amount of code:
- API Tests for checking backend functionality.
- UI Tests for browser interaction and validation.
- Combined Tests to ensure data flows correctly from API to UI.
- API Mocks for creating reliable, deterministic test data.
- Visual Testing for catching layout and pixel changes.
- Performance Tests using the Gatling integration to check load capacity.