Sikuli: Image-Based UI Testing for Desktop and Web Applications
Most test automation frameworks interact with applications through their DOM, accessibility tree, or native platform APIs. Sikuli takes a fundamentally different approach: it finds and interacts with UI elements by recognizing screenshots of them.
If you can see it on screen, Sikuli can click it — regardless of whether the application exposes any programmatic interface.
What Is Sikuli?
Sikuli (now maintained as SikuliX) is an open-source automation tool that uses OpenCV image recognition to find UI elements on screen. You provide screenshots of what you're looking for, and Sikuli locates matching regions and simulates mouse clicks, keyboard input, and other interactions.
This approach works for:
- Desktop applications with no accessibility API
- Legacy enterprise apps (Java Swing, WinForms, old web apps)
- Applications inside virtual machines or remote desktops
- Browser extensions and browser UI (not page content)
- Games and any application where DOM access isn't possible
Installation
SikuliX requires Java 11+. Download the IDE and/or API jar from the SikuliX releases page.
IDE Setup
- Download
sikulixide-2.0.5.jar - Run:
java -jar sikulixide-2.0.5.jar
The IDE provides a visual environment where you can capture screenshots and write scripts.
Script-Only Setup
For scripting without the IDE:
# Download the API jar
wget https://github.com/RaiMan/SikuliX1/releases/download/v2.0.5/sikulixapi-2.0.5.jar
# Run a script
java -jar sikulixapi-2.0.5.jar -r myscript.sikuliWriting Your First Script
Sikuli scripts are Python (using Jython) by default, though Java and Ruby bindings exist.
Capture Screenshots
In the Sikuli IDE, use the Camera icon to capture screenshots of UI elements you want to interact with. The IDE saves these as PNG files alongside your script.
Basic Script
# Find and click a button by its image
click("submit-button.png")
# Type into a field (click first to focus it)
click("email-field.png")
type("user@example.com")
# Find text and verify it exists
if exists("success-message.png"):
print("Login succeeded")
else:
print("Login failed")Image Similarity Threshold
By default, Sikuli requires ~70% image similarity. Adjust for tolerance:
# More tolerant matching (0.0 to 1.0)
Settings.MinSimilarity = 0.7
# Strict matching
Settings.MinSimilarity = 0.95
# Per-operation similarity
click(Pattern("button.png").similar(0.8))Core Operations
Mouse Operations
# Click
click("element.png")
# Double-click
doubleClick("element.png")
# Right-click
rightClick("element.png")
# Hover
hover("element.png")
# Drag and drop
drag("source.png")
dropAt("target.png")
# Click at an offset from a found image
click(Pattern("icon.png").targetOffset(50, 0)) # 50px to the rightKeyboard Operations
# Type text
type("Hello, World!")
# Type with modifiers
type("a", KeyModifier.CTRL) # Ctrl+A (select all)
type(Key.ENTER)
# Special keys
type(Key.TAB)
type(Key.F5)
type(Key.ESCAPE)Waiting and Assertions
# Wait for element to appear (up to 10 seconds)
wait("loading-spinner.png", 10)
# Wait for element to disappear
waitVanish("loading-spinner.png", 30)
# Assert element is present
assert exists("success-banner.png"), "Success banner not found"
# Find all instances
matches = findAll("list-item.png")
for match in matches:
click(match)Region-Based Operations
Working with specific screen regions avoids false matches:
# Define a region: (x, y, width, height)
sidebar = Region(0, 100, 300, 800)
# Find within region only
sidebar.click("settings-icon.png")
# Relative regions
dialog = find("dialog-header.png")
dialog.below(200).click("ok-button.png")Practical Example: Testing a Desktop Application
import sys
from sikuli import *
def test_file_save():
# Open the application
App.open("notepad.exe")
wait("notepad-window.png", 5)
# Type some content
click("text-area.png")
type("Test content")
# Save via keyboard shortcut
type("s", KeyModifier.CTRL)
# Wait for Save dialog
wait("save-dialog.png", 5)
# Enter filename
click("filename-field.png")
type("test-file.txt")
type(Key.ENTER)
# Verify save succeeded
if exists("title-bar-saved.png"):
print("PASS: File saved successfully")
else:
print("FAIL: Save dialog did not close")
sys.exit(1)
test_file_save()Web Application Testing
For web apps, Sikuli can interact with browser UI and handle cases where WebDriver struggles:
# Handle browser file upload dialog (not accessible via WebDriver)
click("upload-button.png")
wait("file-dialog.png", 5)
type("/path/to/file.pdf")
type(Key.ENTER)
# Handle browser permission dialogs
if exists("allow-camera-dialog.png"):
click("allow-button.png")
# Interact with Flash or canvas elements
click("flash-player.png")CI Integration
Running Sikuli in CI requires a real display. Use Xvfb on Linux:
name: Sikuli Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '11'
- name: Install Xvfb
run: sudo apt-get install -y xvfb
- name: Run tests with virtual display
run: |
Xvfb :99 -screen 0 1920x1080x24 &
export DISPLAY=:99
java -jar sikulixapi.jar -r tests/login_test.sikuliImportant: Sikuli in CI is fragile. Screen resolution, DPI settings, font rendering, and OS-level theme differences all affect image matching. Capture screenshots in the same environment where tests will run.
When Image-Based Testing Makes Sense
Do use Sikuli for:
- Legacy applications with no automation API (old Java Swing apps, Access-based tools)
- Testing across the full OS UI stack (desktop apps, system dialogs, tray icons)
- Applications inside RDP/VNC sessions
- Verifying that a UI element is visually rendered correctly, not just present in DOM
- Automation of applications you don't own and can't modify
Don't use Sikuli for:
- Modern web applications (use Playwright, Cypress, or Selenium)
- Mobile apps (use Appium)
- Applications with accessible automation APIs (use those APIs)
- High-volume CI testing (too slow, too fragile)
Limitations
Brittle on UI changes. Any visual change — new button style, moved element, different font rendering — breaks image matches. Maintenance burden is high.
Resolution-dependent. Screenshots captured at 1920x1080 won't match on a 1280x800 CI runner. Managing screen resolution consistency is a real problem.
Slow. Image recognition is computationally expensive. Tests run slower than DOM-based automation.
No debugging information. When a test fails, you get "image not found" — not "element not visible because modal is blocking it." Debugging takes longer.
Not suitable for dynamic content. Any content that changes (timestamps, user names, avatars) needs masking or separate handling.
SikuliX vs Alternatives
| Tool | Approach | Best For |
|---|---|---|
| SikuliX | Image recognition | Legacy/inaccessible apps |
| Appium | Accessibility API | Mobile + desktop apps |
| WinAppDriver | Windows accessibility | Windows desktop apps |
| PyAutoGUI | Screen coordinates | Simple desktop automation |
| Playwright | Browser DOM | Modern web apps |
For Windows desktop applications specifically, Microsoft's WinAppDriver (based on WebDriver) and Appium are generally more reliable than image-based approaches when accessibility APIs are available.
Summary
Sikuli fills a genuine gap: automating applications that have no programmatic interface. For legacy desktop apps, custom in-house tools, or any scenario where you can only interact with the screen output, image-based testing is sometimes the only option.
The trade-off is maintenance cost. Image-based tests require careful screenshot management, tight environment control, and regular updates when UI changes. For applications that rarely change, this is manageable. For actively developed software, DOM-based approaches are almost always preferable.
If you're inheriting a legacy enterprise application with no API and need automated testing, SikuliX is a pragmatic solution.