EXTENSIONS
Image Comparison
Karate provides built-in image comparison for visual regression testing. Compare baseline images against current screenshots to detect unintended UI changes, with detailed diff reports embedded directly in HTML test reports.
On this page:
- compareImage - Compare baseline and latest images
- Comparison engines - Resemble vs SSIM algorithms
- Configuration - Global and per-comparison options
- Ignored regions - Exclude dynamic content
- karate.compareImage() - JavaScript API for programmatic use
- karate.embed() - Embed images in reports
- Troubleshooting - Common issues and solutions
Watch the image comparison video demo for a walkthrough of visual testing with Karate.
compareImage
The compareImage keyword compares two images and embeds a visual diff UI into the HTML report.
Feature: Basic image comparison
Scenario: Compare screenshots
* def baseline = read('classpath:screenshots/login.png')
* def current = karate.readAsBytes('current-login.png')
* compareImage { baseline: '#(baseline)', latest: '#(current)' }
Loading Images
Images can be loaded using path prefixes or as byte arrays:
Feature: Image loading options
Scenario: Different ways to load images
# Using path prefixes
* compareImage { baseline: 'classpath:screenshots/login.png', latest: 'file:/tmp/login.png' }
# Using this: for relative paths
* compareImage { baseline: 'this:baseline.png', latest: 'this:current.png' }
# Using byte arrays from screenshot()
* def latestBytes = screenshot(false)
* compareImage { baseline: 'classpath:screenshots/login.png', latest: '#(latestBytes)' }
# Using karate.readAsBytes()
* def baseline = karate.readAsBytes('classpath:screenshots/login.png')
* compareImage { baseline: '#(baseline)', latest: 'current.png' }
| Prefix | Description | Example |
|---|---|---|
classpath: | Load from classpath (src/test/resources) | classpath:screenshots/login.png |
file: | Load from absolute file path | file:/tmp/screenshot.png |
this: | Load relative to current feature file | this:baseline.png |
Tolerance Threshold
Allow minor pixel differences with failureThreshold:
Feature: Tolerance-based comparison
Scenario: Compare with 2% tolerance
* configure imageComparison = { failureThreshold: 2 }
* def baseline = read('classpath:screenshots/dashboard.png')
* def current = screenshot(false)
* compareImage { baseline: '#(baseline)', latest: '#(current)' }
The failureThreshold is a percentage (0-100). A value of 2 means comparison passes if less than 2% of pixels differ.
Comparison Engines
Karate supports two comparison algorithms, each suited for different use cases.
Resemble Engine
The default engine. Best for pixel-perfect comparisons with anti-aliasing detection and color tolerance.
Feature: Resemble engine
Scenario: Pixel-perfect comparison
* configure imageComparison = { engine: 'resemble' }
* compareImage { baseline: 'baseline.png', latest: 'current.png' }
When to use Resemble:
- Pixel-perfect UI validation
- Detecting color shifts
- Anti-aliasing tolerance needed
- Fine-grained control over color channels
SSIM Engine
Structural Similarity Index. Best for perceptual comparisons where minor pixel shifts are acceptable.
Feature: SSIM engine
Scenario: Structural similarity comparison
* configure imageComparison = { engine: 'ssim' }
* compareImage { baseline: 'baseline.png', latest: 'current.png' }
When to use SSIM:
- Perceptual similarity over pixel-perfect
- Cross-browser comparisons (minor rendering differences)
- Images with compression artifacts
- Faster comparison for large images
Combining Engines
Use both engines together with different behaviors:
Feature: Combined engines
Scenario: Use lowest mismatch percentage
# Comma separator: evaluate both, use the lowest mismatch percentage
* configure imageComparison = { engine: 'resemble,ssim' }
* compareImage { baseline: 'baseline.png', latest: 'current.png' }
Scenario: Fallback engine
# Pipe separator: use second engine only if first fails
* configure imageComparison = { engine: 'resemble|ssim' }
* compareImage { baseline: 'baseline.png', latest: 'current.png' }
| Separator | Behavior |
|---|---|
, (comma) | Run both engines, use the lowest mismatch percentage |
| (pipe) | Use second engine as fallback if first fails |
Configuration Options
Global Configuration
Set defaults for all comparisons using configure imageComparison:
Feature: Global configuration
Background:
* configure imageComparison = { failureThreshold: 1, hideUiOnSuccess: true }
Scenario: All comparisons use global config
* compareImage { baseline: 'page1.png', latest: 'current1.png' }
* compareImage { baseline: 'page2.png', latest: 'current2.png' }
| Option | Type | Default | Description |
|---|---|---|---|
engine | string | 'resemble' | Comparison engine: 'resemble', 'ssim', or combined |
failureThreshold | number | 0.0 | Percentage of pixels allowed to differ (0-100) |
allowScaling | boolean | false | Scale images to match dimensions automatically |
hideUiOnSuccess | boolean | false | Hide comparison UI in reports when images match |
mismatchShouldPass | boolean | false | Treat mismatches as passes (useful for initial baselining) |
onShowRebase | function | null | Custom callback for Rebase button in HTML report |
onShowConfig | function | null | Custom callback for Show Config in HTML report |
Initial Baselining
When creating baselines for the first time, use mismatchShouldPass to prevent failures:
Feature: Initial baseline creation
Background:
# All comparisons pass even if images differ
* configure imageComparison = { mismatchShouldPass: true }
Scenario: Create baselines
* driver 'https://httpbin.org'
* def current = screenshot(false)
# This passes but shows the diff UI in reports for review
* compareImage { baseline: 'baseline.png', latest: '#(current)' }
After reviewing the report and downloading baselines, remove mismatchShouldPass.
Per-Comparison Options
Override global settings for individual comparisons using the options parameter:
Feature: Per-comparison options
Background:
* configure imageComparison = { failureThreshold: 1 }
Scenario: Override for specific comparison
# This comparison ignores colors
* compareImage { baseline: 'base.png', latest: 'current.png', options: { ignoreColors: true } }
Ignored Regions
Exclude dynamic areas like timestamps, ads, or loading animations:
Feature: Ignored regions
Scenario: Exclude dynamic content
* def ignoredAreas =
"""
[
{ "top": 10, "left": 900, "bottom": 40, "right": 1200 },
{ "top": 500, "left": 50, "bottom": 550, "right": 250 }
]
"""
* compareImage { baseline: 'baseline.png', latest: 'current.png', options: { ignoredBoxes: '#(ignoredAreas)' } }
Each region is defined with top, left, bottom, right coordinates in pixels.
Ignoring Colors
Ignore specific colors or all colors:
Feature: Color ignoring
Scenario: Ignore all colors (structure only)
* compareImage { baseline: 'base.png', latest: 'current.png', options: { ignoreColors: true } }
Scenario: Ignore specific color (e.g., purple overlay)
* def purple = { "r": 190, "g": 0, "b": 255 }
* compareImage { baseline: 'base.png', latest: 'current.png', options: { ignoreAreasColoredWith: '#(purple)' } }
Resemble Options
Fine-tune the Resemble engine:
Feature: Resemble options
Scenario: Anti-aliasing detection
* compareImage { baseline: 'base.png', latest: 'current.png', options: { ignoreAntialiasing: true } }
Scenario: Use ignore preset
# Presets: 'nothing', 'less', 'antialiasing', 'colors', 'alpha'
* compareImage { baseline: 'base.png', latest: 'current.png', options: { ignore: 'antialiasing' } }
Scenario: Custom color tolerances
* def tolerances =
"""
{
"red": 4,
"green": 4,
"blue": 4,
"alpha": 4,
"minBrightness": 4,
"maxBrightness": 250
}
"""
* compareImage { baseline: 'base.png', latest: 'current.png', options: { tolerances: '#(tolerances)' } }
| Option | Type | Description |
|---|---|---|
ignoredBoxes | array | Regions to exclude: [{top, left, bottom, right}] |
ignore | string | Preset: 'nothing', 'less', 'antialiasing', 'colors', 'alpha' |
ignoreColors | boolean | Compare brightness only |
ignoreAntialiasing | boolean | Ignore anti-aliasing differences |
ignoreAreasColoredWith | object | Ignore specific color: {r, g, b} or {r, g, b, a} |
tolerances | object | Custom thresholds: {red, green, blue, alpha, minBrightness, maxBrightness} |
SSIM Options
Configure the SSIM algorithm:
Feature: SSIM options
Scenario: High-precision SSIM
* configure imageComparison = { engine: 'ssim' }
* compareImage {
baseline: 'base.png',
latest: 'current.png',
options: { ssim: 'WEBER', rgb2grayVersion: 'INTEGER', k1: 0.01, k2: 0.03, windowSize: 11 }
}
Scenario: Fast SSIM
* configure imageComparison = { engine: 'ssim' }
* compareImage { baseline: 'base.png', latest: 'current.png', options: { ssim: 'FAST' } }
| Option | Type | Default | Description |
|---|---|---|---|
ssim | string | 'WEBER' | Algorithm: 'FAST' or 'WEBER' |
rgb2grayVersion | string | 'INTEGER' | Grayscale conversion: 'ORIGINAL' or 'INTEGER' |
k1 | number | 0.01 | First stability constant |
k2 | number | 0.03 | Second stability constant |
windowSize | integer | 11 | Comparison window size |
bitDepth | integer | 8 | Bits per pixel (8, 16, 32) |
maxSize | integer | 256 | Max dimension before downsampling |
Report Integration
Viewing Diffs
The comparison UI is automatically embedded in HTML reports showing:
- Side-by-side baseline and latest images
- Highlighted diff overlay
- Mismatch percentage
- Rebase and Show Config buttons
Custom Rebase Callback
Customize the filename when rebasing:
Feature: Custom rebase
Scenario: Custom rebase filename
* text onRebaseFn =
"""
function(config, downloadLatestFn) {
var timestamp = new Date().toISOString().replace(/:/g, '-');
downloadLatestFn('baseline-' + timestamp + '.png');
return 'Baseline saved with timestamp: ' + timestamp;
}
"""
* configure imageComparison = { onShowRebase: '#(onRebaseFn)' }
* compareImage { baseline: 'base.png', latest: 'current.png' }
karate.compareImage()
For programmatic control, use the JavaScript API:
Feature: Programmatic comparison
Scenario: Using karate.compareImage()
* def baseline = karate.readAsBytes('classpath:screenshots/login.png')
* def latest = screenshot(false)
* def result = karate.compareImage(baseline, latest)
* print 'Mismatch percentage:', result.mismatchPercentage
* print 'Engine used:', result.engine
* if (result.isMismatch) karate.fail('Images differ by ' + result.mismatchPercentage + '%')
Scenario: With options
* def options = { ignoreColors: true, failureThreshold: 5 }
* def result = karate.compareImage(baseline, latest, options)
* match result.isMismatch == false
The result object contains:
| Property | Description |
|---|---|
baseline | Baseline image bytes |
latest | Latest image bytes |
mismatchPercentage | Percentage of differing pixels |
engine | Engine used for comparison |
failureThreshold | Configured threshold |
isMismatch | true if mismatch exceeds threshold |
isBaselineMissing | true if baseline file not found |
isScaleMismatch | true if dimensions differ |
error | Error message if comparison failed |
karate.embed()
Embed images or other content into HTML reports:
Feature: Embedding content
Scenario: Embed screenshot in report
* def bytes = screenshot(false)
* karate.embed(bytes, 'image/png')
Scenario: Embed with custom label
* def bytes = karate.readAsBytes('classpath:test-image.png')
* karate.embed(bytes, 'image/png')
This is useful for adding custom screenshots or images to reports outside of compareImage.
Screenshot Integration
Combine with UI testing for visual regression:
Feature: Visual regression with screenshots
Background:
* configure driver = { type: 'chrome' }
* configure imageComparison = { failureThreshold: 1, hideUiOnSuccess: true }
Scenario: Compare page screenshot
* driver 'https://httpbin.org'
* def current = screenshot(false)
* compareImage { baseline: 'classpath:baselines/httpbin.png', latest: '#(current)' }
Scenario: Compare element screenshot
* driver 'https://httpbin.org'
* def element = locate('h2')
* def current = element.screenshot()
* compareImage { baseline: 'classpath:baselines/header.png', latest: '#(current)' }
screenshot()- Captures viewport and auto-embeds in reportscreenshot(false)- Captures viewport, returns bytes without embeddingelement.screenshot()- Captures specific element only
Troubleshooting
Images Won't Match
Problem: Images differ unexpectedly
Solutions:
- Check image dimensions match (or enable
allowScaling: true) - Increase
failureThresholdfor minor differences - Use
ignoredBoxesfor dynamic content areas - Try SSIM engine instead of Resemble
- Check HTML report diff to identify specific differences
Performance Issues
Problem: Image comparison slows down tests
Solutions:
- Enable
hideUiOnSuccess: trueto reduce report size - Compare specific elements instead of full page
- Reduce image resolution before comparison
- Run visual tests separately from functional tests
Baseline Maintenance
Problem: Baselines outdated after UI changes
Solutions:
- Set
mismatchShouldPass: truetemporarily while updating - Use Rebase button in HTML report to download new baselines
- Organize baselines by feature/page for easier maintenance
- Version control baseline images with tests
Missing Baseline
Problem: Baseline file not found
Solutions:
- Check path prefix is correct (
classpath:,file:,this:) - Verify file exists in expected location
- Use
karate.readAsBytes()to debug file loading - Check
result.isBaselineMissingwhen usingkarate.compareImage()
Resources
- Video Demo - Visual testing walkthrough
- Working Example - Complete standalone project
Next Steps
- Automate browser testing: UI Testing
- Mock visual dependencies: Test Doubles
- Run visual tests in parallel: Parallel Execution
- Debug comparison failures: Debugging
- See more examples: Examples and Demos