ADVANCED
Debugging and Troubleshooting
What You'll Learn
- Configure logging levels and output for effective debugging
- Use dry run mode to validate test structure without execution
- Control report verbosity and mask sensitive information
- Debug tests in VS Code with breakpoints and step-through
- Implement custom logging strategies for CI/CD pipelines
- Troubleshoot common issues with proven solutions
Overview
Effective debugging and troubleshooting are essential for maintaining robust test suites. Karate provides comprehensive logging capabilities, debugging tools, and configuration options to help you quickly identify and resolve issues in your tests.
Logging Configuration
Basic Logging Setup
Karate uses Logback for logging. Create a logback-test.xml
file in your classpath:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/karate.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.intuit.karate" level="DEBUG"/>
<root level="warn">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
```gherkin
### Logging Levels
Control logging verbosity by adjusting levels:
```xml
<!-- Detailed logging for debugging -->
<logger name="com.intuit.karate" level="DEBUG"/>
<!-- Reduced logging for CI/CD -->
<logger name="com.intuit.karate" level="INFO"/>
<!-- Minimal logging -->
<logger name="com.intuit.karate" level="WARN"/>
```gherkin
### Environment-Specific Logging
Use Janino for conditional logging configuration:
```xml
<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>
</configuration>
```gherkin
## Logging in Tests
### Using karate.log()
Log messages within your tests:
```gherkin
Feature: Logging examples
Scenario: Basic logging
* karate.log('Starting test execution')
* def data = { id: 123, name: 'test' }
* karate.log('Processing data:', data)
* url apiUrl
* method get
* status 200
* karate.log('Response received:', response)
```gherkin
### Debug vs Print
Choose the appropriate logging method:
```gherkin
Scenario: Logging strategies
# Always appears in console and reports
* print 'This always shows'
# Can be suppressed with configure printEnabled
* karate.log('Standard log message')
# Only shows when logger level is DEBUG
* karate.logger.debug('Debug information')
# Pretty print JSON/XML
* def data = { complex: { nested: 'structure' } }
* print 'Formatted data:', data
```gherkin
### Conditional Logging
Log based on conditions:
```gherkin
Background:
* def debugMode = karate.properties['debug.mode'] || false
* def logDebug =
"""
function(msg, data) {
if (karate.get('debugMode')) {
karate.log('DEBUG:', msg, data);
}
}
"""
Scenario: Conditional logging
* logDebug('Request payload', request)
* url apiUrl
* method post
* logDebug('Response', response)
```gherkin
## Report Verbosity
### Controlling Report Output
Configure what appears in HTML reports:
```gherkin
Feature: Report configuration
Background:
# Full verbose reporting
* configure report = { showLog: true, showAllSteps: true }
Scenario: Detailed reporting
* print 'This appears in report'
* def data = 'internal variable' # This also appears
* match response == '#present'
Scenario: Minimal reporting
* configure report = { showLog: false, showAllSteps: false }
* print 'Only print statements show'
* def hidden = 'not in report' # Hidden from report
```gherkin
### Selective Reporting
Control reporting for specific sections:
```gherkin
Scenario: Selective report control
# Show authentication details
* configure report = true
* url authUrl
* request credentials
* method post
* status 200
# Hide large response payload
* configure report = false
* url dataUrl
* method get
* status 200
* def largeData = response
# Re-enable for important validation
* configure report = true
* match largeData.summary.total == 100
```gherkin
### Feature-Level Report Control
Use tags to hide entire features from reports:
```gherkin
@report=false
Feature: Utility functions
# This entire feature is hidden from HTML reports
Scenario: Hidden utility
* def helper = function(x) { return x * 2 }
* def result = helper(5)
```gherkin
## Log Masking
### Masking Sensitive Data
Implement custom log masking for security:
```java
// DemoLogModifier.java
public class DemoLogModifier implements HttpLogModifier {
@Override
public boolean enableForUri(String uri) {
// Only activate for sensitive endpoints
return uri.contains("/auth") || uri.contains("/user");
}
@Override
public String request(String msg) {
// Mask passwords in requests
return msg.replaceAll("\"password\"\\s*:\\s*\"[^\"]+\"",
"\"password\":\"***MASKED***\"");
}
@Override
public String response(String msg) {
// Mask tokens in responses
return msg.replaceAll("\"token\"\\s*:\\s*\"[^\"]+\"",
"\"token\":\"***MASKED***\"");
}
}
```gherkin
Use the log modifier in tests:
```gherkin
Feature: Secure logging
Background:
* def LogModifier = Java.type('com.example.DemoLogModifier')
* configure logModifier = new LogModifier()
Scenario: Login with masked credentials
* url authUrl
* request { username: 'user@example.com', password: 'secret123' }
* method post
* status 200
# Password and token are masked in logs/reports
```gherkin
### Built-in Masking
Use configure to mask specific keys:
```gherkin
Background:
# Mask specific JSON keys
* configure mask = ['password', 'token', 'apiKey', 'secret']
Scenario: Automatic masking
* def sensitive = { password: 'secret', apiKey: 'key123' }
* print sensitive # Values are masked in output
```gherkin
## Dry Run Mode
### Validating Test Structure
Run tests without execution to validate structure:
```gherkin
Feature: Dry run validation
Background:
* configure dryRun = true
Scenario: Structure validation only
* url 'https://api.example.com' # Not executed
* method get # Syntax validated
* status 200 # Structure checked
* match response == { id: '#number' } # Syntax validated
```gherkin
### Programmatic Dry Run
Use dry run in Runner API:
```java
Results results = Runner.path("classpath:features")
.dryRun(true)
.parallel(1);
// Check for syntax errors
assertEquals(0, results.getFailCount());
```gherkin
## VS Code Debugging
### Setting Up Debug Configuration
Create `.vscode/launch.json`:
```json
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Debug Karate Test",
"request": "launch",
"mainClass": "com.intuit.karate.Main",
"args": [
"classpath:features/debug-test.feature",
"--debug"
],
"projectName": "karate-tests"
}
]
}
```gherkin
### Breakpoint Debugging
Add breakpoints in JavaScript functions:
```gherkin
Feature: Debug with breakpoints
Background:
* def calculateTotal =
"""
function(items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
// Set breakpoint here in VS Code
total += items[i].price * items[i].quantity;
}
return total;
}
"""
Scenario: Debug calculation
* def items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 3 }
]
* def total = calculateTotal(items)
* match total == 35
```gherkin
### Interactive Debugging
Use karate.pause() for interactive debugging:
```gherkin
Scenario: Interactive debug session
* def data = { complex: 'structure' }
* karate.log('About to pause for debugging')
* eval karate.pause() # Execution pauses here
* print 'Continuing after pause'
```gherkin
## Performance Profiling
### Timing Analysis
Track execution time for performance debugging:
```gherkin
Feature: Performance profiling
Background:
* def timer =
"""
{
start: function(name) {
karate.set(name + '_start', new Date().getTime());
},
end: function(name) {
var start = karate.get(name + '_start');
var duration = new Date().getTime() - 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 + '/large-dataset'
* method get
* status 200
* def dataTime = timer.end('data')
* assert authTime < 1000 # Auth should be fast
* assert dataTime < 5000 # Data fetch within limits
```gherkin
### Memory Usage Tracking
Monitor memory consumption:
```gherkin
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
```gherkin
## Troubleshooting Common Issues
### Connection Issues
Debug connection problems:
```gherkin
Feature: Connection debugging
Background:
* configure connectTimeout = 30000
* configure readTimeout = 30000
* configure ssl = true
* configure logPrettyRequest = true
* configure logPrettyResponse = true
Scenario: Debug connection
* karate.log('Testing connection to:', apiUrl)
* url apiUrl + '/health'
* method get
* retry until responseStatus == 200 || karate.log('Retry attempt')
```gherkin
### JSON Path Issues
Debug JSONPath expressions:
```gherkin
Scenario: JSONPath debugging
* def data = read('complex.json')
# Debug JSONPath step by step
* def level1 = data.root
* print 'Level 1:', level1
* def level2 = level1.nested
* print 'Level 2:', level2
* def result = karate.jsonPath(data, '$.root.nested[?(@.active==true)]')
* print 'JSONPath result:', result
```gherkin
### Match Failures
Debug match expressions:
```gherkin
Scenario: Match debugging
* def actual = { id: 123, name: 'test', extra: 'field' }
* def expected = { id: '#number', name: '#string' }
# Use karate.match for detailed error info
* def matchResult = karate.match(actual, expected)
* if (!matchResult.pass) karate.log('Match failed:', matchResult.message)
# Alternative: use contains for partial matching
* match actual contains expected
```gherkin
## Best Practices
### Logging Strategy
```gherkin
# ✅ Good: Structured logging
Background:
* def log =
"""
{
info: function(msg, data) {
karate.log('[INFO]', msg, data || '');
},
error: function(msg, error) {
karate.log('[ERROR]', msg, error);
},
debug: function(msg, data) {
if (karate.properties['debug']) {
karate.log('[DEBUG]', msg, data || '');
}
}
}
"""
# ❌ Avoid: Excessive logging
* print request
* print response
* print headers
* print cookies
```gherkin
### Error Handling
```gherkin
# ✅ Good: Informative error messages
* if (response.error) karate.fail('API Error: ' + response.error.message + ' (Code: ' + response.error.code + ')')
# ❌ Avoid: Generic failures
* if (response.error) karate.fail('Error occurred')
```gherkin
## CI/CD Integration
### Pipeline-Friendly Logging
Configure logging for CI/CD environments:
```javascript
// karate-config.js
function fn() {
var config = {};
if (karate.env === 'ci') {
// Minimal logging in CI
karate.configure('printEnabled', false);
karate.configure('report', { showLog: false, showAllSteps: false });
// But capture failures
karate.configure('afterScenario', function() {
if (karate.info.errorMessage) {
karate.log('FAILED:', karate.info.scenarioName);
karate.log('ERROR:', karate.info.errorMessage);
}
});
}
return config;
}
```gherkin
## Next Steps
- Review [Hooks and Lifecycle](/docs/advanced/hooks) for test orchestration
- Explore [Parallel Execution](/docs/running-tests/parallel-execution) debugging
- Learn about [Test Reports](/docs/running-tests/parallel-execution#test-reports) configuration