re-frame.test-support¶
re-frame.test-support is the runtime-state testing surface. It holds test-only fixture machinery and test-flavoured helpers that drive and assert against a frame's app-db, the registrar, the dispatch drain, and the trace stream. Its sibling re-frame.test-helpers owns the view-tree axis: the hiccup walkers plus the testid authoring helper.
This namespace does not re-export from re-frame.core, so a production build never picks up test-flavoured machinery by accident. A test file requires it alongside [re-frame.core :as rf] (and [re-frame.test-helpers :as th] for view assertions).
Examples below also use [re-frame.core :as rf] for the production primitives that double as testing entry points (dispatch-sync, app-db-value, …). For the practical how-to, see Test an event handler and Test a pipeline run.
Fixture machinery¶
The fixture primitives follow one pattern: snapshot the registrar before the test mutates registrations, then restore it afterwards, whether the test passes or fails. Framework-shipped registrations are captured in the snapshot, so they survive. Per-test registrations are rolled back.
snapshot-registrar¶
- Kind: function
- Signature:
- Description: Capture the current registrar state. Returns a snapshot value for later restore.
- Example:
restore-registrar!¶
- Kind: function
- Signature:
- Description: Restore a previously captured registrar
snapshot. Returnsnil. - Example:
make-reset-runtime-fixture¶
- Kind: function
- Signature:
- Description: Build a
clojure.test/cljs.test:eachfixture that resets the per-process runtime around each test. Pair withuse-fixtures :each.
Around each test the fixture:
- reinstates the stable ns-load registrar and source-store baseline captured at fixture-build time, which makes tests independent of run order inside a shared test bundle;
- snapshots the registrar;
- resets the frames registry, trace listeners, and per-artefact state (flows, schemas, machines, routing, resources, http, epoch). This runs via late-bind hooks that no-op when an artefact is absent from the classpath;
- disposes then reinstalls the adapter;
- restores everything in a
finally.
opts (all optional):
| Key | Meaning |
|---|---|
:adapter |
Substrate adapter to install; also ensures the :rf/default frame. When omitted, no adapter is installed. |
:init-fn |
Zero-arg fn run after adapter install, before the test body, under the same ambient frame scope as the body. |
:clear-kinds |
Collection of registrar kinds cleared after the snapshot capture and before the body (the snapshot restores them on the way out). |
:clear-app-schemas? |
Boolean; clear the schemas artefact's per-frame side-table for the test's duration. |
:ambient-frame |
Frame id bound as the body's ambient scope when an adapter is installed. Default :rf/default; pass nil to opt out (for tests that create their own top-level frames). |
:async? |
Boolean, default false. Selects the return shape: false returns the synchronous fn-form fixture; true returns a cljs.test map-form fixture {:before … :after …}, required for suites with (async done …) tests. |
| - Example: | |
Test-flavoured helpers¶
To fire several events in order, call rf/dispatch-sync per event — each drains to fixed point before the next, so observable state between calls reflects committed effects:
assert-path-equals¶
- Kind: function
- Signature:
- Description: Assert
(get-in db path) == expected-valagainst the resolved frame'sapp-db. A mismatch fires aclojure.test/is-style failure viado-report. Returnstrueon pass andfalseotherwise; the failure has already been reported either way.
opts: :frame targets a non-default frame; frame resolution is :frame opt → (current-frame) → :rf/default.
This is the fn-side counterpart to the :rf.assert/path-equals story event-family: same name root, different runner channel.
- Example:
(rf/dispatch-sync [:counter/inc])
(ts/assert-path-equals [:n] 1)
;; against a non-default frame:
(ts/assert-path-equals [:cart :count] 2 {:frame :checkout})
For a full-db assertion, compare directly: (is (= expected-db (rf/app-db-value frame-id))).
Deterministic-wait helpers¶
poll-until¶
- Kind: function
- Signature:
-
Description: Poll
preduntil it returns truthy, within a bounded deadline. -
JVM: synchronous. Returns the truthy value, or throws
ex-infoon timeout. - CLJS: returns a
js/Promisethat resolves with the truthy value, or rejects on timeout. Apredthat returns ajs/Promiseis awaited; its resolved value drives the truthy check.
The timeout error carries :rf.error/id :rf.error/poll-until-timeout (the canonical discriminator), plus :elapsed-ms and :label in its data.
opts: :timeout-ms (default 2000), :interval-ms (default 5), :label.
- Example:
;; JVM — synchronous; returns the truthy value (throws on timeout).
(ts/poll-until #(= 2 (:n (rf/app-db-value :rf/default)))
{:label "counter reached 2"})
;; CLJS — returns a js/Promise; compose with cljs.test/async.
(-> (ts/poll-until #(= 3 (:n (rf/app-db-value :rf/default))))
(.then (fn [_] (done))))
Trace-recorder bracket¶
with-trace-recorder!¶
- Kind: macro
- Signature:
- Description: Bracket
bodywith a fresh trace-tooling listener that accumulates matching trace events into an atom bound torecs-sym. The listener is registered beforebodyruns and unregistered in afinallyon the way out, even ifbodythrows. Returns the value ofbody's final form.
opts (optional map literal; keys evaluated at macroexpansion):
:pred— a 1-arg(fn [ev] truthy?)filter. Default: accept every event.:shape—:flat(default; the atom holds a vector of events) or:by-op(the atom holds a map keyed by(:operation ev)).:key— listener key. Default: a freshly-gensym'd keyword unique to the expansion site, so two brackets in one test do not collide.
Macro requires:
- JVM: resolves alias-qualified through the normal
(:require [re-frame.test-support :as ts]). - CLJS: the namespace carries no self-
:require-macros(unlikere-frame.core). CLJS test files must therefore require the macro explicitly:(:require-macros [re-frame.test-support :refer [with-trace-recorder!]]), or:as tsin:require-macrosfor alias-qualified use. - Example:
;; Flat shape (default), default filter, simple read. (ts/with-trace-recorder! [traces] (rf/dispatch-sync [:my-event]) (is (= 1 (count (filter #(= :rf.event/run-start (:operation %)) @traces))))) ;; :by-op shape with a :pred filter — atom holds a map keyed by operation. (ts/with-trace-recorder! [observed {:pred #(contains? #{:rf.view/render :rf.view/rendered} (:operation %)) :shape :by-op}] (render-twice!) (is (= 2 (count (:rf.view/render @observed)))) (is (= 2 (count (:rf.view/rendered @observed)))))
See also¶
- re-frame.core.md — the production primitives that double as testing entry points (
make-frame,with-frame,dispatch-sync,with-fx-overrides,app-db-value,compute-sub,sub-topology) and the registrar-introspection API (registrations,handler-meta). - re-frame.test-helpers.md — the sibling view-tree assertion namespace (hiccup walkers plus the
testidauthoring helper). - re-frame.http.md — HTTP test stubs for tests that exercise managed requests.
- Test an event handler and Test a pipeline run — the practical how-to guides for the testing surface.