Skip to content

Testing

pythonnative.testing renders components without a device or simulator. render mounts an element into an in-memory FakeBackend and returns a RenderResult with Testing Library-style queries and event helpers; render_hook does the same for a bare hook. within scopes the queries to one subtree, wait_for polls a predicate while draining the framework loop, and fake_clock puts asyncio timers on virtual time.

Test utilities: render components without a device.

import pythonnative as pn
from pythonnative.testing import render, render_hook, wait_for, within

@pn.component
def Counter():
    count, set_count = pn.use_state(0)
    return pn.Column(
        pn.Text(f"Count: {count}"),
        pn.Button("+", on_press=lambda: set_count(count + 1)),
    )

def test_counter_increments():
    result = render(Counter())
    result.press(result.get_by_role("button", name="+"))
    assert result.get_by_text("Count: 1")

def test_use_state():
    hook = render_hook(lambda: pn.use_state("a"))
    hook.act(lambda: hook.current[1]("b"))
    assert hook.current[0] == "b"

def test_timer(pn_clock):
    result = render(Toast())            # hides itself after asyncio.sleep(3)
    pn_clock.advance(3.0)               # timers fire, no real sleep
    assert wait_for(lambda: result.query_by_text("Saved") is None)
  • render mounts an element into a FakeBackend and returns a RenderResult with Testing Library-style queries (get_by_text, get_by_test_id, get_by_label, get_by_type, get_by_role, get_by_placeholder_text, get_by_display_value, each with query_by_, get_all_by_, and find_by_ variants) and event helpers (press, fire, change_text, back, act).
  • within scopes the same queries to one view's subtree; wait_for polls a predicate while draining the framework loop.
  • render_hook runs a hook in a throwaway component.
  • settle pumps the framework loop so async work (resources, queries, transitions) completes.
  • fake_clock (and the pn_clock pytest fixture) installs a FakeClock so asyncio timers run on virtual time.
  • FakeHost stands in for a native screen host so root stack navigators can be tested.
  • pythonnative.testing.pytest_plugin is registered with pytest automatically and resets the runtime between tests.

Modules:

Name Description
backend

In-memory backend implementing the batched mutation protocol.

clock

Virtual time for the framework loop.

harness

Render components into a fake backend and query the result.

pytest_plugin

pytest plugin shipped with PythonNative (registered under the pytest11 entry point).

queries

Testing Library-style queries over a tree of fake views.

Classes:

Name Description
FakeBackend

Tag-table backend recording one tuple per applied mutation.

FakeView

Simulated native view: type, props, children, and last frame.

FakeClock

Controllable clock installed on the framework's headless loop.

FakeHost

Application lifecycle and restoration host for navigation tests.

HookResult

Handle returned by render_hook.

RenderResult

Handle to a mounted tree: queries, events, re-render, unmount.

Queries

Query methods shared by RenderResult and within scopes.

Scope

Queries restricted to one view's subtree (see within).

Functions:

Name Description
current_fake_clock

Return the installed FakeClock, or None.

fake_clock

Install a FakeClock on the framework loop for the block.

render

Mount element into a FakeBackend.

render_hook

Run hook(*args, **kwargs) inside a throwaway component.

settle

Pump the framework loop and flush pending renders until everything is idle.

wait_for

Poll predicate until it returns a truthy value, draining the framework loop between checks.

within

Scope queries to view's subtree.

Attributes:

Name Type Description
DEFAULT_INTRINSIC Dict[str, Tuple[float, float]]

Intrinsic sizes reported for content-sized leaves (what platform measure hooks would return).

IMPLICIT_ROLES Dict[str, str]

Accessibility role each native type implies when no accessibility_role prop is set.

Matcher

What a query accepts: an exact string, a compiled regex (search), or a predicate over the candidate.

DEFAULT_INTRINSIC module-attribute

DEFAULT_INTRINSIC: Dict[str, Tuple[float, float]] = {
    "Text": (60.0, 16.0),
    "Button": (80.0, 32.0),
    "Image": (40.0, 40.0),
    "TextInput": (120.0, 32.0),
    "TabBar": (320.0, 49.0),
}

Intrinsic sizes reported for content-sized leaves (what platform measure hooks would return).

IMPLICIT_ROLES module-attribute

IMPLICIT_ROLES: Dict[str, str] = {
    "Button": "button",
    "Pressable": "button",
    "Picker": "button",
    "DatePicker": "button",
    "Switch": "switch",
    "Checkbox": "checkbox",
    "TextInput": "textbox",
    "Image": "image",
    "ImageBackground": "image",
    "Slider": "adjustable",
    "ProgressBar": "progressbar",
}

Accessibility role each native type implies when no accessibility_role prop is set.

get_by_role consults this table after the explicit prop, so pn.Button("Save") matches get_by_role("button") and pn.Switch() matches get_by_role("switch"). Text has no implicit role; pn.Text("Title", accessibility_role="header") matches get_by_role("header") through the prop.

Matcher module-attribute

Matcher = Union[
    str, Pattern[str], Callable[[Optional[str]], bool]
]

What a query accepts: an exact string, a compiled regex (search), or a predicate over the candidate.

FakeBackend

FakeBackend(
    intrinsic: Optional[
        Dict[str, Tuple[float, float]]
    ] = None,
)

Tag-table backend recording one tuple per applied mutation.

Parameters:

Name Type Description Default
intrinsic Optional[Dict[str, Tuple[float, float]]]

Override the intrinsic sizes used by measure_intrinsic (defaults to DEFAULT_INTRINSIC).

None

Methods:

Name Description
apply_mutations

Apply one committed batch to the view tree, recording each op in ops and batches.

resolve_view

Return the live view registered under tag, or None.

measure_intrinsic

Return the configured intrinsic size for the view's type and record the call in measure_calls.

command

Record an imperative view command in commands and answer it for known view types.

request_list

Simulate a native viewport using the shared list request protocol.

set_animated_property

Record an animated property write in animated without touching props.

start_animation

Decline native animation (return False) so animations run through the Python driver.

cancel_animation

Do nothing; the fake never starts native animations.

live_view_count

Return how many views are currently registered (created and not yet destroyed).

ops_of

Every recorded op tuple whose first element is kind.

detached_views

Live views never inserted into a parent (Portal overlays, the root).

Attributes:

Name Type Description
focused_tag Optional[int]

Tag of the TextInput that last received focus (None after blur).

selections Dict[int, Tuple[int, int]]

(start, end) selection per TextInput tag, set by set_selection and select_all.

scroll_offsets Dict[int, Dict[str, float]]

Content offset per ScrollView tag, updated by scroll_to_offset and scroll_to_end.

focused_tag instance-attribute

focused_tag: Optional[int] = None

Tag of the TextInput that last received focus (None after blur).

selections instance-attribute

selections: Dict[int, Tuple[int, int]] = {}

(start, end) selection per TextInput tag, set by set_selection and select_all.

scroll_offsets instance-attribute

scroll_offsets: Dict[int, Dict[str, float]] = {}

Content offset per ScrollView tag, updated by scroll_to_offset and scroll_to_end.

apply_mutations

apply_mutations(ops: Sequence[Mutation]) -> None

Apply one committed batch to the view tree, recording each op in ops and batches.

Raises AssertionError on malformed transactions (unknown tags, double creates or destroys).

resolve_view

resolve_view(tag: int) -> Optional[FakeView]

Return the live view registered under tag, or None.

measure_intrinsic

measure_intrinsic(
    tag: int, max_width: float, max_height: float
) -> Tuple[float, float]

Return the configured intrinsic size for the view's type and record the call in measure_calls.

Unknown tags and types without an entry measure as (0.0, 0.0); the constraints are ignored.

command

command(
    tag: int,
    name: str,
    args: Optional[Dict[str, Any]] = None,
) -> Any

Record an imperative view command in commands and answer it for known view types.

TextInput: get_value returns the value prop; focus and blur track focused_tag and fire on_focus / on_blur when the app wired them; clear empties the value; select_all and set_selection(start, end) record selections. ScrollView: scroll_to_offset(x, y) and scroll_to_end update scroll_offsets; get_scroll_offset returns {"x": ..., "y": ...}. WebView: can_go_back and can_go_forward are False, get_url returns the url prop, load_url(url) replaces it, get_title is None. VirtualList: scroll_to_index and scroll_to_end drive the list request protocol. Anything else returns None.

request_list

request_list(
    tag: int, first: int, *, extent: float = 800.0
) -> None

Simulate a native viewport using the shared list request protocol.

set_animated_property

set_animated_property(
    tag: int, prop_name: str, value: Any
) -> None

Record an animated property write in animated without touching props.

start_animation

start_animation(
    tag: int,
    anim_id: int,
    prop_name: str,
    spec: Dict[str, Any],
) -> bool

Decline native animation (return False) so animations run through the Python driver.

cancel_animation

cancel_animation(tag: int, anim_id: int) -> Any

Do nothing; the fake never starts native animations.

live_view_count

live_view_count() -> int

Return how many views are currently registered (created and not yet destroyed).

ops_of

ops_of(kind: str) -> List[Any]

Every recorded op tuple whose first element is kind.

detached_views

detached_views(
    type_name: Optional[str] = None,
) -> List[FakeView]

Live views never inserted into a parent (Portal overlays, the root).

FakeView

FakeView(tag: int, type_name: str, props: Dict[str, Any])

Simulated native view: type, props, children, and last frame.

Attributes:

Name Type Description
tag

The reconciler-assigned tag (use with fire / events).

type_name

Native type, e.g. "Text".

props Dict[str, Any]

Native-safe props (event callbacks are stripped; they live in the event registry keyed by tag).

children List[FakeView]

Child views in order.

frame Tuple[float, float, float, float]

(x, y, width, height) from the last layout pass.

Methods:

Name Description
walk

Yield this view and every descendant, depth-first.

find_all

Every view in this subtree matching a type name or predicate.

find_first

Return the first view in this subtree matching a type name or predicate, or None.

dump

Indented, human-readable subtree (for failing-test output).

text property

text: Optional[str]

Visible static text: Text.text or Button.title.

A TextInput's contents are exposed separately as value and placeholder so get_by_text never matches a form field.

placeholder property

placeholder: Optional[str]

The placeholder prop of a TextInput (or Picker), if set.

value property

value: Optional[str]

The string value prop (a TextInput's display value), if set.

role property

role: Optional[str]

Effective accessibility role: the accessibility_role prop, else the type's implicit role.

See IMPLICIT_ROLES for the implicit table.

accessible_name property

accessible_name: Optional[str]

What assistive technology would announce for this view.

The accessibility_label prop wins; otherwise the view's own text or title, a Checkbox's label, and finally the text of descendant views joined with single spaces (so a Pressable wrapping Text("Save") is named "Save").

disabled property

disabled: bool

Whether the view is disabled via the disabled prop or accessibility_state.

hidden property

hidden: bool

Whether this view is removed from layout (display: "none").

test_id property

test_id: Optional[str]

The test_id prop, if set.

label property

label: Optional[str]

The accessibility_label prop, if set.

walk

walk(
    *, include_hidden: bool = True
) -> Iterator["FakeView"]

Yield this view and every descendant, depth-first.

With include_hidden=False subtrees under a display: "none" view are skipped (what a user can see).

find_all

find_all(predicate_or_type: Any) -> List['FakeView']

Every view in this subtree matching a type name or predicate.

find_first

find_first(predicate_or_type: Any) -> Optional['FakeView']

Return the first view in this subtree matching a type name or predicate, or None.

dump

dump(indent: int = 0) -> str

Indented, human-readable subtree (for failing-test output).

FakeClock

FakeClock(loop: AbstractEventLoop)

Controllable clock installed on the framework's headless loop.

Create one through fake_clock (or the pn_clock pytest fixture) rather than directly; the context manager installs it on the loop the framework is using and restores the real clock afterwards.

Attributes:

Name Type Description
loop

The asyncio loop whose time() this clock replaces.

Methods:

Name Description
install

Patch loop.time (idempotent). Raises if the loop is running on another thread.

uninstall

Restore the loop's real time() (idempotent).

advance

Move the clock forward by seconds and run everything that becomes due.

run_until_idle

Run ready callbacks and timers due at the current time until the loop has nothing runnable.

now property

now: float

The loop's current time in seconds: monotonic real time plus the virtual offset.

offset property

offset: float

Seconds of virtual time added so far by advance.

install

install() -> None

Patch loop.time (idempotent). Raises if the loop is running on another thread.

uninstall

uninstall() -> None

Restore the loop's real time() (idempotent).

advance

advance(seconds: float) -> None

Move the clock forward by seconds and run everything that becomes due.

Timers fire in order at their scheduled instants (a timer that re-arms itself every second fires twice during advance(2.5)), and the loop is drained after each so the tasks they wake run to their next await. Returns once the clock has reached its target and nothing further is due.

Parameters:

Name Type Description Default
seconds float

How far to advance; must not be negative.

required

Raises:

Type Description
ValueError

If seconds is negative.

run_until_idle

run_until_idle(*, max_iterations: int = 10000) -> None

Run ready callbacks and timers due at the current time until the loop has nothing runnable.

This never blocks: each iteration runs the loop for exactly one turn, so work queued by that turn runs on the next. Timers in the future are left alone.

Raises:

Type Description
RuntimeError

If the loop is still producing work after max_iterations turns.

FakeHost

FakeHost(initial_state: Optional[Dict[str, Any]] = None)

Application lifecycle and restoration host for navigation tests.

Pass as render(..., host=FakeHost()) to provide restoration state. Screens remain in the logical tree. set_focused simulates the application entering or leaving the foreground.

Methods:

Name Description
initial_navigation_state

Return the initial_state the host was constructed with (None for a fresh root).

set_screen_options

Append a copy of options to options (see the title property).

add_focus_listener

Register a focus callback fired by set_focused; returns an unsubscribe callable.

set_focused

Simulate the platform covering (False) or revealing (True) the screen.

Attributes:

Name Type Description
title Optional[str]

The most recent title passed to set_screen_options.

title property

title: Optional[str]

The most recent title passed to set_screen_options.

initial_navigation_state

initial_navigation_state() -> Optional[Dict[str, Any]]

Return the initial_state the host was constructed with (None for a fresh root).

set_screen_options

set_screen_options(options: Dict[str, Any]) -> None

Append a copy of options to options (see the title property).

add_focus_listener

add_focus_listener(
    callback: Callable[[bool], None],
) -> Callable[[], None]

Register a focus callback fired by set_focused; returns an unsubscribe callable.

set_focused

set_focused(focused: bool) -> None

Simulate the platform covering (False) or revealing (True) the screen.

HookResult

HookResult(
    result: RenderResult,
    box: Dict[str, Any],
    rerender: Callable[..., None],
)

Bases: Generic[T]

Handle returned by render_hook.

Attributes:

Name Type Description
current T

The hook's most recent return value.

Methods:

Name Description
act

Run fn (typically a state setter) and settle.

rerender

Re-run the hook with new arguments.

settle

Flush pending renders and async work started by the hook.

unmount

Unmount the harness component, running the hook's effect cleanups.

current property

current: T

The value the hook returned on its most recent render.

render_count property

render_count: int

How many times the hook has run since render_hook was called.

act

act(fn: Callable[[], Any]) -> None

Run fn (typically a state setter) and settle.

rerender

rerender(*args: Any, **kwargs: Any) -> None

Re-run the hook with new arguments.

settle

settle() -> None

Flush pending renders and async work started by the hook.

unmount

unmount() -> None

Unmount the harness component, running the hook's effect cleanups.

RenderResult

RenderResult(
    reconciler: Any,
    backend: FakeBackend,
    wrap: Callable[[Node], Element],
)

Bases: Queries

Handle to a mounted tree: queries, events, re-render, unmount.

Query methods come in four flavors, mirroring Testing Library: get_by_* returns exactly one match or raises LookupError (with the tree dumped in the message), query_by_* returns the match or None, get_all_by_* returns every match, and find_by_* waits for the match with wait_for. Matchers are exact strings, compiled regexes, or predicates. Views inside a display: "none" subtree (inactive tabs, covered stack screens) are skipped unless hidden=True is passed. See Queries for the full list, including get_by_role, get_by_placeholder_text, and get_by_display_value.

Methods:

Name Description
views

Every live view, root first (includes detached Portal overlays).

dump

Indented text rendering of the live tree.

fire

Dispatch event (an on_* prop name) to target and settle.

press

Fire on_press on target.

act

Run fn (state setters, imperative calls) and settle so the next line sees the result.

change_text

Fire on_change on a TextInput.

back

Simulate the system back action; returns whether a handler consumed it.

settle

Flush pending renders and async work (see settle).

rerender

Reconcile a new root element (new props from outside the tree).

unmount

Unmount the tree, running effect cleanups and destroying every view.

Attributes:

Name Type Description
root Optional[FakeView]

The root native view (None after unmount).

root property

The root native view (None after unmount).

views

views(*, hidden: bool = False) -> List[FakeView]

Every live view, root first (includes detached Portal overlays).

dump

dump() -> str

Indented text rendering of the live tree.

fire

fire(target: Target, event: str, *args: Any) -> None

Dispatch event (an on_* prop name) to target and settle.

Payloads are decoded through the element's contract the way the native bridge decodes them, so a handler receives the same types it gets on a device: fire(scroll, "on_scroll", {"x": 0, "y": 40}) delivers a ScrollEvent, not a dict.

Raises:

Type Description
LookupError

If no handler is registered for the event.

TypeError

If the payload doesn't match the event's contract.

press

press(target: Target, *, force: bool = False) -> None

Fire on_press on target.

Like a real tap, the press is refused when the target is disabled (disabled=True or accessibility_state={"disabled": True}) or sits inside a disabled Pressable or Button.

Parameters:

Name Type Description Default
target Target

The view (or tag) to press.

required
force bool

Dispatch even if the target is disabled.

False

Raises:

Type Description
AssertionError

If the target is disabled and force is False.

LookupError

If no on_press handler is registered.

act

act(fn: Callable[[], Any]) -> None

Run fn (state setters, imperative calls) and settle so the next line sees the result.

change_text

change_text(target: Target, value: str) -> None

Fire on_change on a TextInput.

back

back() -> bool

Simulate the system back action; returns whether a handler consumed it.

settle

settle(timeout: float = 1.0) -> None

Flush pending renders and async work (see settle).

rerender

rerender(element: Node) -> None

Reconcile a new root element (new props from outside the tree).

unmount

unmount() -> None

Unmount the tree, running effect cleanups and destroying every view.

Queries

Query methods shared by RenderResult and within scopes.

Subclasses implement views (the candidate views in document order) and dump (used in error messages). See the module docstring for the get_by_ / query_by_ / get_all_by_ / find_by_ conventions.

Methods:

Name Description
views

Candidate views in document order (hidden=True includes display: "none" subtrees).

dump

Indented text rendering of the queried tree.

text

Visible strings in document order (Text contents and Button titles).

get_all_by_text

Return every view whose visible text matches matcher (exact=False matches substrings).

get_by_text

Return the single view whose visible text matches matcher; raise LookupError otherwise.

query_by_text

Return the first view whose visible text matches matcher, or None.

find_by_text

get_by_text retried with wait_for until it succeeds.

get_all_by_test_id

Return every view whose test_id prop matches matcher.

get_by_test_id

Return the single view whose test_id prop matches matcher; raise LookupError otherwise.

query_by_test_id

Return the first view whose test_id prop matches matcher, or None.

find_by_test_id

get_by_test_id retried with wait_for until it succeeds.

get_all_by_label

Return every view whose accessibility_label prop matches matcher.

get_by_label

Return the single view whose accessibility_label matches matcher; raise LookupError if not.

query_by_label

Return the first view whose accessibility_label prop matches matcher, or None.

find_by_label

get_by_label retried with wait_for until it succeeds.

get_all_by_type

Return every view of native type type_name (for example "Text").

get_by_type

Return the single view of native type type_name; raise LookupError otherwise.

query_by_type

Return the first view of native type type_name, or None.

find_by_type

get_by_type retried with wait_for until it succeeds.

get_all_by_role

Return every view whose accessibility role matches role (and accessible name matches name).

get_by_role

Return the single view with role role (and accessible name name); raise LookupError otherwise.

query_by_role

Return the first view with role role (and accessible name name), or None.

find_by_role

get_by_role retried with wait_for until it succeeds.

get_all_by_placeholder_text

Return every view whose placeholder prop matches matcher (exact=False matches substrings).

get_by_placeholder_text

Return the single view whose placeholder matches matcher; raise LookupError otherwise.

query_by_placeholder_text

Return the first view whose placeholder matches matcher, or None.

find_by_placeholder_text

get_by_placeholder_text retried with wait_for until it succeeds.

get_all_by_display_value

Return every view whose string value prop (a TextInput's contents) matches matcher.

get_by_display_value

Return the single view whose display value matches matcher; raise LookupError otherwise.

query_by_display_value

Return the first view whose display value matches matcher, or None.

find_by_display_value

get_by_display_value retried with wait_for until it succeeds.

within

Return the same queries restricted to view and its descendants.

views

views(*, hidden: bool = False) -> List[FakeView]

Candidate views in document order (hidden=True includes display: "none" subtrees).

dump

dump() -> str

Indented text rendering of the queried tree.

text

text(*, hidden: bool = False) -> List[str]

Visible strings in document order (Text contents and Button titles).

get_all_by_text

get_all_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose visible text matches matcher (exact=False matches substrings).

get_by_text

get_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view whose visible text matches matcher; raise LookupError otherwise.

query_by_text

query_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view whose visible text matches matcher, or None.

find_by_text

find_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_text retried with wait_for until it succeeds.

get_all_by_test_id

get_all_by_test_id(
    matcher: Matcher, *, hidden: bool = False
) -> List[FakeView]

Return every view whose test_id prop matches matcher.

get_by_test_id

get_by_test_id(
    matcher: Matcher, *, hidden: bool = False
) -> FakeView

Return the single view whose test_id prop matches matcher; raise LookupError otherwise.

query_by_test_id

query_by_test_id(
    matcher: Matcher, *, hidden: bool = False
) -> Optional[FakeView]

Return the first view whose test_id prop matches matcher, or None.

find_by_test_id

find_by_test_id(
    matcher: Matcher,
    *,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_test_id retried with wait_for until it succeeds.

get_all_by_label

get_all_by_label(
    matcher: Matcher, *, hidden: bool = False
) -> List[FakeView]

Return every view whose accessibility_label prop matches matcher.

get_by_label

get_by_label(
    matcher: Matcher, *, hidden: bool = False
) -> FakeView

Return the single view whose accessibility_label matches matcher; raise LookupError if not.

query_by_label

query_by_label(
    matcher: Matcher, *, hidden: bool = False
) -> Optional[FakeView]

Return the first view whose accessibility_label prop matches matcher, or None.

find_by_label

find_by_label(
    matcher: Matcher,
    *,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_label retried with wait_for until it succeeds.

get_all_by_type

get_all_by_type(
    type_name: str, *, hidden: bool = False
) -> List[FakeView]

Return every view of native type type_name (for example "Text").

get_by_type

get_by_type(
    type_name: str, *, hidden: bool = False
) -> FakeView

Return the single view of native type type_name; raise LookupError otherwise.

query_by_type

query_by_type(
    type_name: str, *, hidden: bool = False
) -> Optional[FakeView]

Return the first view of native type type_name, or None.

find_by_type

find_by_type(
    type_name: str,
    *,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_type retried with wait_for until it succeeds.

get_all_by_role

get_all_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose accessibility role matches role (and accessible name matches name).

The role is the accessibility_role prop, else the type's implicit role from IMPLICIT_ROLES (Button and Pressable are "button", Switch is "switch", Checkbox is "checkbox", TextInput is "textbox", Image is "image"). The name is the accessibility_label, else the visible text or title, else the text of descendants (see FakeView.accessible_name); exact=False matches name as a substring.

get_by_role

get_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view with role role (and accessible name name); raise LookupError otherwise.

query_by_role

query_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view with role role (and accessible name name), or None.

find_by_role

find_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_role retried with wait_for until it succeeds.

get_all_by_placeholder_text

get_all_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose placeholder prop matches matcher (exact=False matches substrings).

get_by_placeholder_text

get_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view whose placeholder matches matcher; raise LookupError otherwise.

query_by_placeholder_text

query_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view whose placeholder matches matcher, or None.

find_by_placeholder_text

find_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_placeholder_text retried with wait_for until it succeeds.

get_all_by_display_value

get_all_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose string value prop (a TextInput's contents) matches matcher.

get_by_display_value

get_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view whose display value matches matcher; raise LookupError otherwise.

query_by_display_value

query_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view whose display value matches matcher, or None.

find_by_display_value

find_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_display_value retried with wait_for until it succeeds.

within

within(view: FakeView) -> 'Scope'

Return the same queries restricted to view and its descendants.

Scope

Scope(view: FakeView)

Bases: Queries

Queries restricted to one view's subtree (see within).

Attributes:

Name Type Description
view

The subtree root; it is itself a candidate for every query.

Methods:

Name Description
views

The scope root and every descendant, depth-first (skipping hidden subtrees unless hidden=True).

dump

Indented text rendering of the scoped subtree.

views

views(*, hidden: bool = False) -> List[FakeView]

The scope root and every descendant, depth-first (skipping hidden subtrees unless hidden=True).

dump

dump() -> str

Indented text rendering of the scoped subtree.

current_fake_clock

current_fake_clock() -> Optional[FakeClock]

Return the installed FakeClock, or None.

fake_clock

fake_clock() -> Iterator[FakeClock]

Install a FakeClock on the framework loop for the block.

The pytest fixture pn_clock (from pythonnative.testing.pytest_plugin) wraps this for the duration of a test.

render

render(
    element: Node,
    *,
    viewport: Optional[
        Tuple[float, float]
    ] = DEFAULT_VIEWPORT,
    backend: Optional[FakeBackend] = None,
    host: Optional[FakeHost] = None,
    settle_first: bool = True
) -> RenderResult

Mount element into a FakeBackend.

Parameters:

Name Type Description Default
element Node

The element (or component call) to render.

required
viewport Optional[Tuple[float, float]]

Size for the layout pass; None skips layout.

DEFAULT_VIEWPORT
backend Optional[FakeBackend]

Reuse an existing backend (defaults to a fresh one).

None
host Optional[FakeHost]

Render under a HostRoot with this fake host for focus and restoration state.

None
settle_first bool

Drain async work and effects before returning.

True

render_hook

render_hook(
    hook: Callable[..., T],
    *args: Any,
    host: Optional[FakeHost] = None,
    **kwargs: Any
) -> HookResult[T]

Run hook(*args, **kwargs) inside a throwaway component.

result = render_hook(lambda: pn.use_state(0))
value, set_value = result.current
result.act(lambda: set_value(5))
assert result.current[0] == 5

settle

settle(
    reconciler: Any = None, timeout: float = 1.0
) -> None

Pump the framework loop and flush pending renders until everything is idle.

Use after triggering async work (use_resource, use_query, transitions, coroutines started by handlers) so assertions see the settled tree. Under a fake_clock only work due at the clock's current time runs; timers wait for clock.advance.

wait_for

wait_for(
    predicate: Callable[[], T],
    *,
    timeout: float = 1.0,
    interval: float = 0.01
) -> T

Poll predicate until it returns a truthy value, draining the framework loop between checks.

LookupError and AssertionError raised by the predicate count as "not yet", so wait_for(lambda: result.get_by_text("Saved")) works as well as wait_for(lambda: result.query_by_text("Saved")). Other exceptions propagate immediately.

Parameters:

Name Type Description Default
predicate Callable[[], T]

Called on every check; its truthy return value is the result.

required
timeout float

Real seconds to keep trying (measured on the wall clock, not a fake one).

1.0
interval float

Longest pause between checks; the loop is drained during it.

0.01

Returns:

Type Description
T

The first truthy value the predicate returned.

Raises:

Type Description
TimeoutError

With the last error (if any) and the rendered tree, when timeout elapses first.

within

within(view: FakeView) -> Scope

Scope queries to view's subtree.

card = within(result.get_by_test_id("summary"))
assert card.get_by_display_value("a@b.c")

Queries

Every query comes in four flavors: get_by_* (exactly one match or LookupError), query_by_* (the match or None), get_all_by_* (every match), and find_by_* (get_by_* wrapped in wait_for). RenderResult and the scope returned by within share this class.

Query methods shared by RenderResult and within scopes.

Subclasses implement views (the candidate views in document order) and dump (used in error messages). See the module docstring for the get_by_ / query_by_ / get_all_by_ / find_by_ conventions.

Methods:

Name Description
views

Candidate views in document order (hidden=True includes display: "none" subtrees).

dump

Indented text rendering of the queried tree.

text

Visible strings in document order (Text contents and Button titles).

get_all_by_text

Return every view whose visible text matches matcher (exact=False matches substrings).

get_by_text

Return the single view whose visible text matches matcher; raise LookupError otherwise.

query_by_text

Return the first view whose visible text matches matcher, or None.

find_by_text

get_by_text retried with wait_for until it succeeds.

get_all_by_test_id

Return every view whose test_id prop matches matcher.

get_by_test_id

Return the single view whose test_id prop matches matcher; raise LookupError otherwise.

query_by_test_id

Return the first view whose test_id prop matches matcher, or None.

find_by_test_id

get_by_test_id retried with wait_for until it succeeds.

get_all_by_label

Return every view whose accessibility_label prop matches matcher.

get_by_label

Return the single view whose accessibility_label matches matcher; raise LookupError if not.

query_by_label

Return the first view whose accessibility_label prop matches matcher, or None.

find_by_label

get_by_label retried with wait_for until it succeeds.

get_all_by_type

Return every view of native type type_name (for example "Text").

get_by_type

Return the single view of native type type_name; raise LookupError otherwise.

query_by_type

Return the first view of native type type_name, or None.

find_by_type

get_by_type retried with wait_for until it succeeds.

get_all_by_role

Return every view whose accessibility role matches role (and accessible name matches name).

get_by_role

Return the single view with role role (and accessible name name); raise LookupError otherwise.

query_by_role

Return the first view with role role (and accessible name name), or None.

find_by_role

get_by_role retried with wait_for until it succeeds.

get_all_by_placeholder_text

Return every view whose placeholder prop matches matcher (exact=False matches substrings).

get_by_placeholder_text

Return the single view whose placeholder matches matcher; raise LookupError otherwise.

query_by_placeholder_text

Return the first view whose placeholder matches matcher, or None.

find_by_placeholder_text

get_by_placeholder_text retried with wait_for until it succeeds.

get_all_by_display_value

Return every view whose string value prop (a TextInput's contents) matches matcher.

get_by_display_value

Return the single view whose display value matches matcher; raise LookupError otherwise.

query_by_display_value

Return the first view whose display value matches matcher, or None.

find_by_display_value

get_by_display_value retried with wait_for until it succeeds.

within

Return the same queries restricted to view and its descendants.

views

views(*, hidden: bool = False) -> List[FakeView]

Candidate views in document order (hidden=True includes display: "none" subtrees).

dump

dump() -> str

Indented text rendering of the queried tree.

text

text(*, hidden: bool = False) -> List[str]

Visible strings in document order (Text contents and Button titles).

get_all_by_text

get_all_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose visible text matches matcher (exact=False matches substrings).

get_by_text

get_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view whose visible text matches matcher; raise LookupError otherwise.

query_by_text

query_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view whose visible text matches matcher, or None.

find_by_text

find_by_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_text retried with wait_for until it succeeds.

get_all_by_test_id

get_all_by_test_id(
    matcher: Matcher, *, hidden: bool = False
) -> List[FakeView]

Return every view whose test_id prop matches matcher.

get_by_test_id

get_by_test_id(
    matcher: Matcher, *, hidden: bool = False
) -> FakeView

Return the single view whose test_id prop matches matcher; raise LookupError otherwise.

query_by_test_id

query_by_test_id(
    matcher: Matcher, *, hidden: bool = False
) -> Optional[FakeView]

Return the first view whose test_id prop matches matcher, or None.

find_by_test_id

find_by_test_id(
    matcher: Matcher,
    *,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_test_id retried with wait_for until it succeeds.

get_all_by_label

get_all_by_label(
    matcher: Matcher, *, hidden: bool = False
) -> List[FakeView]

Return every view whose accessibility_label prop matches matcher.

get_by_label

get_by_label(
    matcher: Matcher, *, hidden: bool = False
) -> FakeView

Return the single view whose accessibility_label matches matcher; raise LookupError if not.

query_by_label

query_by_label(
    matcher: Matcher, *, hidden: bool = False
) -> Optional[FakeView]

Return the first view whose accessibility_label prop matches matcher, or None.

find_by_label

find_by_label(
    matcher: Matcher,
    *,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_label retried with wait_for until it succeeds.

get_all_by_type

get_all_by_type(
    type_name: str, *, hidden: bool = False
) -> List[FakeView]

Return every view of native type type_name (for example "Text").

get_by_type

get_by_type(
    type_name: str, *, hidden: bool = False
) -> FakeView

Return the single view of native type type_name; raise LookupError otherwise.

query_by_type

query_by_type(
    type_name: str, *, hidden: bool = False
) -> Optional[FakeView]

Return the first view of native type type_name, or None.

find_by_type

find_by_type(
    type_name: str,
    *,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_type retried with wait_for until it succeeds.

get_all_by_role

get_all_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose accessibility role matches role (and accessible name matches name).

The role is the accessibility_role prop, else the type's implicit role from IMPLICIT_ROLES (Button and Pressable are "button", Switch is "switch", Checkbox is "checkbox", TextInput is "textbox", Image is "image"). The name is the accessibility_label, else the visible text or title, else the text of descendants (see FakeView.accessible_name); exact=False matches name as a substring.

get_by_role

get_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view with role role (and accessible name name); raise LookupError otherwise.

query_by_role

query_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view with role role (and accessible name name), or None.

find_by_role

find_by_role(
    role: Matcher,
    *,
    name: Optional[Matcher] = None,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_role retried with wait_for until it succeeds.

get_all_by_placeholder_text

get_all_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose placeholder prop matches matcher (exact=False matches substrings).

get_by_placeholder_text

get_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view whose placeholder matches matcher; raise LookupError otherwise.

query_by_placeholder_text

query_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view whose placeholder matches matcher, or None.

find_by_placeholder_text

find_by_placeholder_text(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_placeholder_text retried with wait_for until it succeeds.

get_all_by_display_value

get_all_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> List[FakeView]

Return every view whose string value prop (a TextInput's contents) matches matcher.

get_by_display_value

get_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> FakeView

Return the single view whose display value matches matcher; raise LookupError otherwise.

query_by_display_value

query_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False
) -> Optional[FakeView]

Return the first view whose display value matches matcher, or None.

find_by_display_value

find_by_display_value(
    matcher: Matcher,
    *,
    exact: bool = True,
    hidden: bool = False,
    timeout: float = 1.0,
    interval: float = 0.01
) -> FakeView

get_by_display_value retried with wait_for until it succeeds.

within

within(view: FakeView) -> 'Scope'

Return the same queries restricted to view and its descendants.

pytest plugin

Installing pythonnative registers this plugin under the pytest11 entry point, so every pytest session gets the runtime-reset fixture and pn_clock without a conftest.py. Disable it with -p no:pythonnative.

pytest plugin shipped with PythonNative (registered under the pytest11 entry point).

Installing pythonnative activates it in every pytest session. It provides:

  • an autouse fixture that turns on development diagnostics for each test (prop and style validation, hook-order checks, and warnings, as in a debug build), then resets the framework afterwards: the headless asyncio loop is shut down (cancelling leaked tasks), and the event registry, default query client, and recorded diagnostics warnings are cleared, so one test's leftovers never reach the next;
  • pn_clock, a FakeClock installed on the framework loop for the test (see fake_clock).

Nothing here needs to be imported by a conftest.py; pytest loads the plugin from the package metadata. Disable it with -p no:pythonnative.

Functions:

Name Description
pn_clock

A FakeClock driving the framework loop's timers for this test.

pn_clock

pn_clock() -> Iterator[FakeClock]

A FakeClock driving the framework loop's timers for this test.

Next steps