Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

ImGui Test Engine

ImGui Test Engine is the official testing and automation framework for Dear ImGui. It enables automated UI testing, screenshot capture, and regression testing.

Quick Start

Python
C++
from imgui_bundle import imgui, hello_imgui

my_test: imgui.test_engine.Test

def my_register_tests():
    global my_test
    engine = hello_imgui.get_imgui_test_engine()
    my_test = imgui.test_engine.register_test(engine, "My Tests", "Click Button")

    def test_func(ctx: imgui.test_engine.TestContext):
        ctx.item_click("**/My Button")

    my_test.test_func = test_func

def gui():
    test_engine = hello_imgui.get_imgui_test_engine()
    if imgui.button("My Button"):
        print("Clicked!")
    if imgui.button("Run Tests"):
        imgui.test_engine.queue_test(test_engine, my_test)

params = hello_imgui.RunnerParams()
params.use_imgui_test_engine = True
params.callbacks.register_tests = my_register_tests
params.callbacks.show_gui = gui
hello_imgui.run(params)

Full demo

For more complete examples, see the test engine demo from the interactive explorer:

Named References

Test Engine uses “named references” to find widgets. The syntax uses path separators:

PatternMeaning
"Button"Widget named “Button” in current reference
"**/Button"Search for “Button” anywhere in children
"//Window/Button"Absolute path from root
"$FOCUSED"Currently focused window
ctx.set_ref("My Window")           # Set current reference
ctx.item_click("**/Save")          # Find "Save" anywhere in window
ctx.item_click("//Dialog/OK")      # Absolute path to OK button

Common Actions

# Click items
ctx.item_click("**/Button")
ctx.item_double_click("**/Item")

# Input text
ctx.item_input("**/Name", "John Doe")

# Check/uncheck
ctx.item_check("**/Enable")
ctx.item_uncheck("**/Disable")

# Open/close tree nodes
ctx.item_open("**/Settings")
ctx.item_close("**/Settings")
ctx.item_open_all("**/Tree")

# Screenshots
ctx.capture_screenshot_window("Window Name")

# Keyboard input
ctx.key_press(imgui.Key.enter)
ctx.key_chars("Hello")

Assertions

Use the CHECK function to verify conditions:

from imgui_bundle.imgui.test_engine_checks import CHECK

def test_func(ctx: imgui.test_engine.TestContext):
    ctx.item_click("**/Increment")
    CHECK(counter == 1)  # Verify counter was incremented

Custom Test GUI

Tests can display their own GUI:

my_test = imgui.test_engine.register_test(engine, "Category", "Test Name")


def test_gui_func(ctx: imgui.test_engine.TestContext):
    imgui.text("Custom test controls here")
    if imgui.button("Do Something"):
        # Custom action
        pass


my_test.gui_func = test_gui_func

Driving a GUI and capturing screenshots — immapp.testing

ImGui Bundle ships a small testing module that combines the test engine with screenshot capture. Use it when you want to script an interaction (click, type, expand a header) and grab a PNG at chosen moments: automated tests, documentation pictures. It is also how an AI agent sees and drives the app it writes, with nobody at the mouse: the bundle’s screenshot skills (.claude/skills/) are built on it.

Example:

from imgui_bundle import imgui
from imgui_bundle.immapp import testing

def gui():
    imgui.button("Click me")

def my_test(ctx: imgui.test_engine.TestContext):
    testing.capture(ctx, "/tmp/00_initial.png")
    ctx.item_click("//**/Click me")
    testing.capture(ctx, "/tmp/01_after_click.png")

testing.run(gui, my_test, window_size=(600, 400))

See the full demo in demo_immapp_testing.py and demo_immapp_testing.cpp.

Running Tests

When test engine is enabled:

  1. A “Test Engine” window appears

  2. Click tests to run them

  3. Watch as actions are automated

  4. Check results (green = pass, red = fail)

In the browser (Pyodide)

The test engine works in the browser since v1.93, with the same API. Its coroutine, a thread on the desktop, is a WebAssembly stack switch there (JSPI), which needs Chrome 137, Firefox 153 or Safari 27. In an older browser, the app runs without the engine: use_imgui_test_engine is turned off, get_imgui_test_engine() returns None, and a message says so on stderr.

immapp.testing.run() cannot block in the browser: it returns at once, the test runs in the frames that follow, the app stays open after it, and the result is printed when the test ends (stderr on failure). The screenshots go to Pyodide’s virtual file system, and a “Captures” window shows them when the test ends: click one to enlarge it, or download it.

While the engine runs, Python’s automatic garbage collection is replaced by explicit collections at each frame (a workaround for a Pyodide bug with suspended stacks, pyodide#6464): a test that calls gc.enable() reopens it.

Documentation & Resources