Skip to content

Tutorial: First Test

This tutorial writes a Notepad test from scratch. It introduces the four objects you will use most often: Desktop, Application, Window, and Locator.

Create The Test File

Create tests/test_notepad.py in a Dolphin test project:

tests/test_notepad.py
import pytest

pytestmark = pytest.mark.integration


def test_notepad_type_and_read(launch):
    app = launch("notepad.exe")
    win = app.window(class_name="Notepad")
    editor = win.get_by_role("Document")

    editor.click()
    editor.type_text("Hello, Dolphin!")

    assert "Hello, Dolphin!" in editor.text()

Run it:

pytest tests/test_notepad.py -v

What Each Object Does

Desktop is the entry point for launching and connecting to applications. In pytest, the desktop fixture creates one Desktop for the test session.

Application represents a running process. The launch fixture returns an Application and registers cleanup.

Window wraps a top-level window or dialog. app.window(class_name="Notepad") waits for a matching Notepad window.

Locator is a lazy reference to an element inside a window. win.get_by_role("Document") does not search immediately. The search happens when click(), type_text(), text(), or another action/query is called.

Lazy Locators And Auto-Waiting

This line only creates a locator:

editor = win.get_by_role("Document")

This line resolves it:

editor.click()

When an action resolves a locator, Dolphin keeps retrying until the element appears or the timeout expires. The default timeout is 10 seconds and can be changed with --dolphin-timeout, DOLPHIN_TIMEOUT, dolphin_desktop.config(timeout=...), or locator.timeout(...).

Cleanup

Use the launch fixture for tests that own the application process:

def test_example(launch):
    app = launch("notepad.exe")
    win = app.window(class_name="Notepad")
    win.get_by_role("Document").type_text("temporary text")

After the test, the fixture calls Application.kill() for every app it launched. This is deliberate: desktop applications often show save prompts or custom close dialogs, so test teardown should be deterministic.

If you are not using pytest, use the Application context manager or call kill() yourself:

from dolphin_desktop import Desktop

desktop = Desktop()

with desktop.launch("notepad.exe") as app:
    win = app.window(class_name="Notepad")
    win.get_by_role("Document").type_text("Hello")

Finding Better Selectors

Use the spy command to inspect a window:

dolphin spy --window "Notepad" --depth 3

Interactive pick mode can produce locator code for the selected element:

dolphin spy --pick

Prefer selectors in this order when your app exposes them:

  1. get_by_automation_id(...)
  2. get_by_role(..., name=...)
  3. get_by_title(...) or get_by_text(...)
  4. get_by_class(...)
  5. window.image(...) as a fallback when accessibility APIs cannot see the control

Page Object Version

The standard scaffold generated by dolphin init wraps the Notepad editor:

class NotepadPage:
    def __init__(self, window):
        self._editor = window.get_by_role("Document")

    def type_text(self, text: str):
        self._editor.click()
        self._editor.type_text(text)
        return self

    def read_text(self) -> str:
        return self._editor.text()

The test becomes:

def test_notepad_with_page_object(launch):
    app = launch("notepad.exe")
    win = app.window(class_name="Notepad")
    page = NotepadPage(win)

    page.type_text("Hello, Dolphin!")
    assert "Hello, Dolphin!" in page.read_text()

Next, read Core Concepts or the Locator API.