Skip to main content

RUNNING TESTS

Debugging

Debug tests quickly using print statements, IDE breakpoints, and built-in inspection tools—no external debuggers required.

Benefits of Karate Debugging

  • Zero setup: No external debugger installation or configuration needed
  • Built-in pretty printing: Automatic JSON/XML formatting for readability
  • IDE integration: Native support for VS Code and IntelliJ breakpoints
  • Always visible output: Print statements appear in console and HTML reports
  • No performance overhead: Debug features don't slow down production test runs

Simple Print Debugging

Start with the most straightforward approach—print statements for quick inspection.

Feature: Quick debugging

Scenario: Debug with print
* def user = { id: 123, name: 'Alice' }
* print 'Testing user:', user.name

Given url baseUrl + '/users/' + user.id
* print 'Request URL:', baseUrl + '/users/123'
When method get

* print 'Status:', responseStatus
* print 'Response time:', responseTime, 'ms'
Then status 200

Variable Inspection

Use built-in functions to inspect complex data structures and understand test state.

Feature: Inspect variables

Scenario: Debug complex objects
* def order = { id: 'ord_123', items: [{ name: 'Laptop', price: 999 }], total: 999 }

# Pretty print for readability
* print 'Order data:', karate.pretty(order)

# Inspect object structure
* print 'Available keys:', karate.keysOf(order)
* print 'Items count:', order.items.length

# Type checking
* print 'Order type:', karate.typeOf(order)
* print 'Total type:', karate.typeOf(order.total)
Print vs Log

Use print for always-visible output in console and reports. Use karate.log() for debug-level messages that can be suppressed via logging configuration.

IDE Debugging

VS Code Breakpoints

Set breakpoints in JavaScript functions and step through code execution.

Feature: VS Code debugging

Background:
* def calculateDiscount =
"""
function(price, isVip) {
var discount = 0;
if (isVip) {
discount = price * 0.15; // Set breakpoint here
} else {
discount = price * 0.05;
}
return discount;
}
"""

Scenario: Debug discount calculation
* def price = 100
* def isVip = true
* def discount = calculateDiscount(price, isVip)
* match discount == 15

IntelliJ Integration

Debug tests directly from IntelliJ using JUnit integration.

@Test
void debugInIntelliJ() {
// Right-click and select "Debug"
Karate.run("problematic-feature")
.karateEnv("dev")
.relativeTo(getClass());
}

Debug Strategies

Isolate the Problem

Narrow down issues by testing components independently.

Feature: Problem isolation

@debug
Scenario: Test step by step
# Step 1: Verify environment
* print 'Environment:', karate.env
* print 'Base URL:', baseUrl

# Step 2: Test connectivity
Given url baseUrl + '/health'
When method get
* print 'Health check:', responseStatus
Then status 200

# Step 3: Test authentication
* call read('authenticate.feature')
* print 'Auth token valid:', authToken != null

# Step 4: Test target endpoint
Given url baseUrl
And path 'problematic/endpoint'
When method get
* print 'Endpoint status:', responseStatus

Step-by-Step Analysis

Enable detailed logging to see exactly what Karate sends and receives.

Feature: Detailed request/response logging

Scenario: Analyze HTTP traffic
* configure logPrettyRequest = true
* configure logPrettyResponse = true

* def userData = { name: 'Alice', email: 'alice@example.com' }
* print 'Sending data:', karate.pretty(userData)

Given url baseUrl
And path 'users'
And request userData
* print 'Full URL:', baseUrl + '/users'

When method post
* print 'Response time:', responseTime, 'ms'

Then status 201
* print 'Created user:', response
Debugging Parallel Tests

Set .parallel(1) in your test runner to execute sequentially when debugging. Parallel execution interleaves print statements, making output hard to follow.

Common Debugging Patterns

Debug Authentication Flows

Track authentication through multiple steps to identify where it breaks.

Feature: Debug authentication

Scenario: Debug login flow
* print 'Step 1: Request auth token'
Given url baseUrl
And path 'auth/login'
And request { username: 'alice', password: 'secret' }
When method post
* print 'Login status:', responseStatus
* print 'Token received:', response.token != null

* def token = response.token
* print 'Step 2: Use token for API call'
Given url baseUrl
And path 'users/me'
And header Authorization = 'Bearer ' + token
When method get
* print 'Authenticated request status:', responseStatus

Debug JSON Path Issues

Understand why JSON path expressions fail by inspecting data structure.

Feature: Debug JSON path

Scenario: Troubleshoot path expressions
* def response = { data: { users: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }] } }

# Debug step by step
* print 'Full response:', karate.pretty(response)
* print 'Data level:', response.data
* print 'Users array:', response.data.users
* print 'First user:', response.data.users[0]

# Test JSON path
* def result = karate.jsonPath(response, '$.data.users[?(@.id==1)]')
* print 'JSON path result:', result
* match result[0].name == 'Alice'

Debug Match Failures

Get detailed information when assertions fail.

Feature: Debug match failures

Scenario: Understand match errors
* def actual = { id: 123, name: 'Alice', role: 'admin', extra: 'field' }
* def expected = { id: '#number', name: '#string' }

# Use karate.match for detailed error info
* def matchResult = karate.match(actual, expected)
* if (!matchResult.pass) print 'Match failed:', matchResult.message

# Use contains for partial matching
* match actual contains expected
* print 'Partial match succeeded'

# Debug exact match failure
* print 'Actual keys:', karate.keysOf(actual)
* print 'Expected keys:', karate.keysOf(expected)

Debug Flaky Tests

Identify intermittent failures by adding retry logging and timing information.

Feature: Debug flaky tests

Scenario: Track retry attempts
* def attempt = 0
* def maxAttempts = 3

Given url baseUrl
And path 'unstable/endpoint'
And retry until responseStatus == 200 || attempt++ && karate.log('Retry attempt', attempt)
When method get

* print 'Succeeded after', attempt, 'attempts'
* print 'Response time:', responseTime, 'ms'
Then status 200

Log Configuration

Basic Logback Setup

Create logback-test.xml in src/test/resources for detailed logging.

<?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>

<!-- Debug level for Karate -->
<logger name="com.intuit.karate" level="DEBUG"/>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

Log Levels

  • DEBUG: Complete request/response payloads, perfect for troubleshooting
  • INFO: Basic execution information, good for normal test runs
  • WARN: Warnings and timeouts only
  • ERROR: Failures and exceptions only

Debug Tools and Utilities

Built-in Functions

Feature: Debug utilities

Scenario: Use Karate debug functions
* def data = { nested: { value: 'test', count: 42 } }

# Pretty print JSON
* print karate.pretty(data)

# Get object keys
* print karate.keysOf(data)
* print karate.keysOf(data.nested)

# Type checking
* print karate.typeOf(data)
* print karate.typeOf(data.nested.count)

# Size/length
* def array = [1, 2, 3, 4, 5]
* print 'Array length:', array.length

Conditional Debug Output

Enable debug output only when needed using environment variables.

Feature: Conditional debugging

Background:
* def debug = karate.properties['debug'] == 'true'
* def debugLog = function(msg, data) { if (debug) karate.log(msg, data) }

Scenario: Conditional logging
* debugLog('Starting test with user', { id: 123 })

Given url baseUrl
And path 'users/123'
When method get

* debugLog('Response received', response)
Then status 200

Error Message Analysis

Common error patterns and how to debug them:

JSON Path Errors

Error: $.user.id path not found

Debug:

* print 'Full response:', karate.pretty(response)
* print 'Response keys:', karate.keysOf(response)

Connection Failures

Error: Connection timeout or refused

Debug:

* print 'Target URL:', baseUrl
* print 'Response status:', responseStatus
* configure connectTimeout = 30000
* configure readTimeout = 30000

Assertion Failures

Error: Match failed for complex object

Debug:

* print 'Expected:', expected
* print 'Actual:', actual
* def diff = karate.match(actual, expected)
* print 'Match result:', diff

Next Steps

Master debugging and continue with related topics: