posit-dev / posit-dev/py-shiny
feat: Add test mode with export_test_values() and /dataobj/shinytest snapshot endpoint
@schloerke is already working on this.
Since Jun 2, 2026.
- Dominant language
- Python
- Stars
- 1.8k
- Forks
- 135
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 21
Description
Summary
Port R Shiny's test mode to py-shiny: a read-only per-session JSON snapshot endpoint exposing {input, output, export}, plus an export_test_values() API so app authors can surface internal reactive values. This is the server-side foundation for a future shinytest2-like snapshot-testing story in Python.
This is Phase A (server-side snapshot endpoint + API). First-class Playwright/automation access to these values is a later Phase B and is out of scope here.
Motivation
R Shiny's test mode powers shinytest2 snapshot testing: it registers a per-session /dataobj/shinytest JSON endpoint returning {input, output, export}, and exportTestValues() lets app authors expose internal reactive values for snapshotting. py-shiny has the building blocks (input serialization, cached output values, a per-session request router) but nothing is wired up, and there is no export_test_values() equivalent.
Scope (Phase A)
Enabling
- Enable via environment variable
SHINY_TESTMODE=1only (mirrors the existingSHINY_DEV_MODEpattern inhtml_dependencies.py). - A constructor argument (
App(..., test_mode=...)) is deferred to a later phase. - When off: zero behavioral change — no value recording, the endpoint 404s,
export_test_values()is a cheap no-op.
export_test_values() API
- Module-level function and session method (
session.export_test_values(**kwargs)). - Called as
export_test_values(my_val=lambda: total() + 1, count=my_calc)— each value is a zero-arg callable /reactive.calc, evaluated lazily inside a reactive context at snapshot time. - Module-level form resolves the current session via
get_current_session(). - Export names are namespaced with the module's
nsprefix (consistent with inputs/outputs; note R does not namespace). - Last-registration-wins on duplicate names.
Snapshot endpoint
- Auto-registered at
/session/<token>/dataobj/shinytest(R-compatible path) when test mode is on. - Returns
{ "input": {...}, "output": {...}, "export": {...} }(R's singular keys, kept as-is). - Handler wraps work in
session_context(self)+isolate()(same pattern asdynamic_route). input— serialized current inputs (dedicated snapshot serialization path, not the bookmark_serialize, so it doesn't write files to astate_diror silently drop unserializables).output— last-known-value record: maintain a per-session dict of the most recent value computed for each output (recorded where output values are set). Outputs that never computed are absent. (Matches R semantics.)export— each registered callable evaluated lazily at request time.
Serialization fidelity
- Best-effort coercion; values that can't be JSON-encoded are wrapped in a visible tagged marker (e.g.
{"__shiny_serialization_error__": "<message>"}) rather than silently stringified or hard-failing. - Sort keys for deterministic, cleanly-diffable snapshots.
URL helper
- Add
session.get_test_snapshot_url()(R parity withsession$getTestSnapshotUrl()), including a cache-bustingnonce. Included in Phase A.
Out of scope (deferred to Phase B)
- Registering the built-but-unused
shiny/www/shared/shiny-testmode.jseval-driving helper (for driving an app from a parent frame). - First-class Playwright controller /
pagehelper access to snapshot values. App(test_mode=...)constructor argument.
Related
- #1156 (Add testing API for py-shiny — broader testing-namespace request)
- #1281 (Epic — Testing shiny apps; Playwright controller work)
- #1980 (Save and restore state — touches the same input
_serializepath)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.