ADVANCED
Java API & Utilities
Invoke Karate from Java code, configure comprehensive logging, mask sensitive data, and optimize test execution for CI/CD pipelines.
Benefits of Java API
- Programmatic test execution: Full control over test runs from Java code
- Enterprise logging integration: Configure Logback for compliance and monitoring
- Security compliance: Mask passwords, tokens, and sensitive data automatically
- CI/CD optimization: Reduce noise in pipeline logs while capturing failures
- Performance monitoring: Track execution time and memory usage
Quick Start with jbang
Run Karate tests without a build tool using jbang for rapid prototyping and simple projects.
// Install jbang: https://www.jbang.dev
// Run a single feature file
jbang karate@karatelabs test.feature
// Run with specific environment
jbang karate@karatelabs --env=qa test.feature
// Run with tags
jbang karate@karatelabs --tags=@smoke features/
// Parallel execution
jbang karate@karatelabs --threads=5 features/
Runner API Basics
Execute Karate tests programmatically from Java code using the Runner API.
import com.intuit.karate.Results;
import com.intuit.karate.Runner;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
@Test
void runKarateTests() {
Results results = Runner.path("classpath:features")
.tags("~@ignore")
.parallel(5);
assertEquals(0, results.getFailCount(),
results.getErrorMessages());
}
Advanced Runner Configuration
@Test
void advancedRunnerConfig() {
Results results = Runner.path("classpath:api", "classpath:smoke.feature")
.tags("@regression", "~@wip")
.karateEnv("staging")
.outputCucumberJson(true)
.outputJunitXml(true)
.reportDir("target/karate-reports")
.parallel(10);
assertTrue(results.getFailCount() == 0,
"Found " + results.getFailCount() + " failures");
}
Logging Configuration
Basic Logback Setup
Create logback-test.xml
in src/test/resources
for complete logging control.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.intuit.karate" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Environment-Specific Logging
Use Janino for conditional logging based on environment.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<if condition='property("karate.env").equals("ci")'>
<then>
<logger name="com.intuit.karate" level="INFO"/>
</then>
<else>
<logger name="com.intuit.karate" level="DEBUG"/>
</else>
</if>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
File and Console Logging
Send logs to both console and file for comprehensive debugging and archival.
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/karate.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<logger name="com.intuit.karate" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Log Masking for Security
Custom Log Modifier
Implement HttpLogModifier
to mask sensitive data in logs and reports.
public class SecureLogModifier implements HttpLogModifier {
@Override
public boolean enableForUri(String uri) {
// Enable masking for auth endpoints
return uri.contains("/auth") || uri.contains("/login");
}
@Override
public String request(String msg) {
// Mask passwords in request logs
return msg.replaceAll(
"\"password\"\\s*:\\s*\"[^\"]+\"",
"\"password\":\"***MASKED***\""
);
}
@Override
public String response(String msg) {
// Mask tokens in response logs
return msg.replaceAll(
"\"token\"\\s*:\\s*\"[^\"]+\"",
"\"token\":\"***MASKED***\""
);
}
}
Using Log Modifier in Tests
Feature: Secure logging
Background:
* def LogModifier = Java.type('com.example.SecureLogModifier')
* configure logModifier = new LogModifier()
Scenario: Login with masked credentials
Given url authUrl
And path 'login'
And request { username: 'user@test.com', password: 'secret123' }
When method post
Then status 200
# Password is masked in logs and reports
Report Verbosity Control
Configure Report Output
Control what appears in HTML reports for cleaner, more focused test documentation.
Feature: Report configuration
Background:
# Full verbose reporting
* configure report = { showLog: true, showAllSteps: true }
Scenario: Detailed reporting
* print 'This appears in report'
* def data = 'visible variable'
* match response == '#present'
Scenario: Minimal reporting
* configure report = { showLog: false, showAllSteps: false }
* print 'Only print statements visible'
* def internal = 'hidden from report'
Feature-Level Report Control
Hide utility features from reports using tags.
@report=false
Feature: Utility functions
Scenario: Helper function (hidden from reports)
* def helper = function(x) { return x * 2 }
* def result = helper(5)
* match result == 10
Logging in Tests
Using karate.log()
Add structured logging within test scenarios for debugging and monitoring.
Feature: Test logging
Scenario: Structured logging
* karate.log('Starting user creation test')
* def userData = { name: 'Alice', email: 'alice@example.com' }
* karate.log('User data prepared:', userData)
Given url baseUrl
And path 'users'
And request userData
When method post
* karate.log('Response status:', responseStatus)
* karate.log('Created user ID:', response.id)
Then status 201
CI/CD Integration
Pipeline-Friendly Configuration
Reduce log noise in CI while capturing failures for troubleshooting.
// karate-config.js
function fn() {
var config = { baseUrl: 'https://api.example.com' };
if (karate.env === 'ci') {
// Minimal logging in CI
karate.configure('printEnabled', false);
karate.configure('report', {
showLog: false,
showAllSteps: false
});
// Capture failures
karate.configure('afterScenario',
function() {
if (karate.info.errorMessage) {
karate.log('FAILED:', karate.info.scenarioName);
karate.log('ERROR:', karate.info.errorMessage);
}
}
);
}
return config;
}
Parallel Execution Stats
Monitor parallel execution efficiency and identify bottlenecks.
Results results = Runner.path("classpath:features")
.parallel(5);
System.out.println("Scenarios passed: " + results.getPassCount());
System.out.println("Scenarios failed: " + results.getFailCount());
System.out.println("Total time: " + results.getElapsedTime() + "ms");
System.out.println("Features: " + results.getFeaturesPassed() + "/" +
results.getFeaturesTotal());
if (results.getFailCount() > 0) {
System.err.println("Failures:\n" + results.getErrorMessages());
}
Commonly Needed Utilities
Dynamic Port Numbers
Configure dynamic ports for parallel test execution to avoid conflicts.
// karate-config.js
function fn() {
var port = karate.properties['server.port'] || '8080';
var config = {
baseUrl: 'http://localhost:' + port + '/api'
};
return config;
}
// Run tests with dynamic port
// mvn test -Dserver.port=8081
Multiple Functions in One File
Organize reusable JavaScript functions efficiently.
// utils.js
function() {
return {
randomEmail: function() {
return 'user' + Math.floor(Math.random() * 10000) +
'@test.com';
},
randomString: function(length) {
var chars = 'abcdefghijklmnopqrstuvwxyz';
var result = '';
for (var i = 0; i < length; i++) {
result += chars.charAt(
Math.floor(Math.random() * chars.length)
);
}
return result;
},
formatDate: function(date) {
return new Date(date).toISOString().split('T')[0];
}
};
}
Use the utilities in tests:
Background:
* def utils = call read('classpath:utils.js')
Scenario: Use utility functions
* def email = utils.randomEmail()
* def password = utils.randomString(12)
* def today = utils.formatDate(new Date())
* print 'Generated email:', email
* print 'Password length:', password.length
* print 'Today:', today
Java Function References
Call Java static methods directly from Karate tests.
Feature: Java interop
Background:
* def Utils = Java.type('com.example.TestUtils')
* def UUID = Java.type('java.util.UUID')
Scenario: Use Java utilities
* def randomId = UUID.randomUUID().toString()
* def hash = Utils.generateHash('password123')
* def timestamp = Utils.getCurrentTimestamp()
* print 'Random ID:', randomId
* print 'Hash:', hash
When to Use What
Choose the right tool based on your testing needs:
Need | Use This | Why |
---|---|---|
Run tests without build tool | jbang | Zero setup, instant execution |
Programmatic test control | Runner API | Full Java integration |
Mask passwords/tokens | HttpLogModifier | Security compliance |
CI/CD logging | Environment config | Reduce pipeline noise |
Performance tracking | Custom functions | Monitor bottlenecks |
Reusable utilities | utils.js | DRY principle |
Dynamic configuration | karate-config.js | Environment switching |
Always mask sensitive data (passwords, tokens, API keys) in logs and reports. Use HttpLogModifier
for production test suites to ensure compliance.
Performance Monitoring
Track Execution Time
Monitor test performance and identify slow operations.
Background:
* def timer =
"""
{
start: function(name) {
karate.set(name + '_start', Date.now());
},
end: function(name) {
var start = karate.get(name + '_start');
var duration = Date.now() - start;
karate.log(name + ' took ' + duration + 'ms');
return duration;
}
}
"""
Scenario: Profile API calls
* timer.start('auth')
* call read('auth.feature')
* def authTime = timer.end('auth')
* timer.start('data')
* url apiUrl + '/data'
* method get
* def dataTime = timer.end('data')
* assert authTime < 1000
* assert dataTime < 5000
Memory Usage Tracking
Monitor memory consumption during test execution.
Background:
* def memory =
"""
function() {
var runtime = java.lang.Runtime.getRuntime();
var used = runtime.totalMemory() - runtime.freeMemory();
return Math.round(used / 1024 / 1024) + ' MB';
}
"""
Scenario: Memory monitoring
* def initialMemory = memory()
* print 'Initial memory:', initialMemory
# Load large dataset
* def largeData = read('classpath:large-file.json')
* def afterLoadMemory = memory()
* print 'After load:', afterLoadMemory
# Process data
* def processed = largeData.map(x => x.value * 2)
* def finalMemory = memory()
* print 'Final memory:', finalMemory
Advanced Java Integration
Thread-Safe Java Functions
Create reusable Java utilities with proper multi-threading support.
package com.example.karate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class TestUtils {
// Thread-safe counter
private static final AtomicInteger counter = new AtomicInteger(0);
// Thread-safe cache
private static final Map<String, Object> cache =
new ConcurrentHashMap<>();
public static int getNextId() {
return counter.incrementAndGet();
}
public static void cacheValue(String key, Object value) {
cache.put(key, value);
}
public static Object getCachedValue(String key) {
return cache.get(key);
}
}
Use in tests:
Background:
* def Utils = Java.type('com.example.karate.TestUtils')
Scenario: Thread-safe operations
* def id1 = Utils.getNextId()
* def id2 = Utils.getNextId()
* assert id2 == id1 + 1
* Utils.cacheValue('token', 'abc123')
* def token = Utils.getCachedValue('token')
* match token == 'abc123'
Custom Hooks
Implement custom hooks for test lifecycle management.
package com.example.karate;
import com.intuit.karate.RuntimeHook;
import com.intuit.karate.core.ScenarioRuntime;
public class CustomHook implements RuntimeHook {
@Override
public boolean beforeScenario(ScenarioRuntime sr) {
System.out.println("Starting: " + sr.scenario.getName());
sr.engine.setVariable("startTime",
System.currentTimeMillis());
return true;
}
@Override
public void afterScenario(ScenarioRuntime sr) {
long start = (Long) sr.engine.getVariable("startTime");
long duration = System.currentTimeMillis() - start;
System.out.println("Completed in: " + duration + " ms");
}
}
Use the hook:
Results results = Runner.path("classpath:features")
.hook(new CustomHook())
.parallel(5);
Next Steps
Master Java integration and continue with:
- Debug tests effectively: Debugging
- Configure test lifecycle: Hooks
- Optimize parallel execution: Parallel Execution
- Review test reports: Test Reports
- Explore Karate object API: Karate Object