Tutorials
Object Repository
The Object Repository keeps UI selectors in YAML files instead of test code. Tests refer to elements by alias; when the application's UI changes, you edit one YAML entry instead of hunting selectors across fifty test files.
Use it when selectors are shared by many tests, maintained by people who don't write Python, or generated by tooling. For selectors that belong to one screen and one team, Page Objects may read better — the two patterns also combine (a Page Object can resolve its locators from aliases).
1. Define aliases in YAML
Create an objects/ directory next to your tests:
login_window:
selector:
title: "Login - MyApp"
children:
email_input:
selector:
automation_id: txtEmail
role: Edit
fallback:
- name: Email
- name: "E-mail address"
password_input:
selector:
automation_id: txtPassword
submit_button:
selector:
automation_id: btnLogin
name: Login
Each alias has a required selector mapping and optional fallback and
children. Selector keys accept YAML-friendly names — automation_id,
role, name — which map to the underlying criteria (auto_id,
control_type, title); the native names work too. A typo in a key
(automationid:) fails at load time with the file and path in the
message, not deep inside a test.
fallback is a list of alternative selectors tried in order when the
primary one stops matching — the same self-healing mechanism as
fallback selectors, just declared in data.
2. Load the repository
from dolphin_desktop import objects
objects.discover("objects") # every *.yaml in the directory
# or explicitly:
objects.load("objects/login.yaml")
print(objects.available()) # ['login_window', ...]
Call it once per session — conftest.py is the natural home:
from dolphin_desktop import objects
def pytest_configure(config):
objects.discover("objects")
3. Use aliases in tests
Application.window() takes an alias as its first positional argument, and
the returned window resolves element() lookups against that alias's
children:
def test_login(launch):
app = launch("myapp.exe")
win = app.window("login_window") # alias → title criteria
win.element("email_input").type_text("user@example.com")
win.element("password_input").type_text("secret")
win.element("submit_button").click()
An unknown alias raises AliasNotFoundError naming the alias and the files
that were searched.
4. Override hierarchy
Repositories load at one of three levels — workspace < project <
test. A higher level wins for the same alias, so a test suite can
override one selector from shared defaults without forking the file:
objects.load("shared/objects.yaml", level="workspace")
objects.load("objects/app.yaml", level="project") # the default
objects.load("objects/quirks.yaml", level="test") # highest priority
objects.clear() empties everything; objects.clear(level="test") drops
one layer.
5. Live reload while authoring
objects.enable_watch()
With watch mode on, edits to loaded YAML files are picked up on the next
alias lookup — tune a selector against a running application without
restarting the pytest session. Turn it off in CI (objects.disable_watch()
or simply never enable it); production runs should be deterministic.
Where the aliases come from
You don't have to write selectors by hand: dolphin spy --pick prints the
selector of any element you Ctrl+Click, and dolphin spy -w "My Window"
dumps the whole tree — copy the automation_id / name pairs straight
into YAML. See Tracing and Debugging for the spy workflow.
API summary
| Call | Purpose |
|---|---|
objects.load(path, level="project") |
Load one YAML file |
objects.discover(directory="objects") |
Load every *.yaml in a directory, returns the count |
objects.available() |
All registered aliases |
objects.clear(level=None) |
Forget everything, or one level |
objects.enable_watch() / disable_watch() |
Auto-reload on file change |
app.window("alias") |
Resolve a window alias |
win.element("alias") |
Resolve a child alias of the window's entry |
Full reference: dolphin_desktop.objects.