Skip to main content

Module testing

Module testing 

Source
Expand description

Test helpers for Ocre apps, like Rails’ ActionDispatch::IntegrationTest and ActiveSupport::Testing: a request client for the running app, the test database, the server log, time travel and assertions.

Feature testing, native builds only: a generated app lists ocre = { ..., features = ["testing"] } under [dev-dependencies], so the module is in cargo test and never in the WebAssembly build.

§How request tests run

Handlers need workerd (D1, KV, Queues… exist only there), so request tests talk HTTP to the real runtime. ocre test --e2e:

  1. creates a fresh local D1 database in .wrangler/test-state (the development data in .wrangler/state is untouched), applies the migrations and loads the fixtures of tests/fixtures/,
  2. starts one cf dev on that state for the whole run, logging to .wrangler/test-state/dev.log,
  3. runs cargo test -- --ignored with TEST_URL, TEST_STATE and TEST_LOG set. In an Ocre app, #[ignore] marks the tests that need the runtime: plain cargo test (and ocre test) skips them.
// tests/posts.rs
use ocre::testing::Client;

#[test]
#[ignore = "request test: run with `ocre test --e2e`"]
fn creates_a_post() {
    let mut client = Client::new();
    let created = client.post("/posts", &[("title", "Hello"), ("body", "First post")]);
    created.assert_redirect_to("/posts/1");
    assert_eq!(client.flash("notice").as_deref(), Some("Post was successfully created."));
    client.follow_redirect(&created).assert_status(200).assert_contains("Hello");
}

There are no per-test transactions (Rails’ transactional tests): the database belongs to the cf dev process, which the test process reaches only over HTTP or through wrangler. The database is fresh for each run; tests running in parallel share it, so they create their own records (factories give unique values) and assert on them rather than on global counts, or run with -- --test-threads=1.

Structs§

Broadcast
A message broadcast to a realtime channel, from Client::broadcasts.
Client
An HTTP client for request tests, with a cookie jar, like a browser tab (Rails’ integration session).
EnqueuedJob
A job sent to a queue, from Client::jobs.
Jobs
Jobs the app enqueued and ran, from Client::jobs.
Log
The test server’s log (cf dev output: console.log, job and cron lines, errors), read from the position of Log::mark on.
PerformedJob
A job run by the queue consumer, from Client::jobs.
Response
A response in a request test, with chainable assertions (Rails’ assert_response, assert_redirected_to, assert_match).

Constants§

TEST_LOG
Environment variable holding the path of the test server’s log.
TEST_STATE
Environment variable holding the local state directory of the test run (.wrangler/test-state).
TEST_URL
Environment variable holding the base URL of the server ocre test --e2e started.

Functions§

assert_changes
Asserts that block changes what expression returns (Rails’ assert_changes), and returns (before, after).
assert_difference
Asserts that block changes the number expression returns by difference (Rails’ assert_difference), and returns the block’s value.
assert_no_changes
Asserts that block leaves what expression returns unchanged (Rails’ assert_no_changes).
assert_no_difference
Asserts that block leaves the number expression returns unchanged (Rails’ assert_no_difference).
block_on
Runs a future to completion on the test thread: async handlers, helpers and ocre::password in plain unit tests (no async runtime runs outside workerd). It is pollster::block_on.
count
Number of rows of table in the test database, for assert_difference.
eventually
Retries check every 100 ms for up to 30 seconds until it returns Some, for effects that happen later: a queued job, a cron, a broadcast.
fixture
The row of table loaded from the fixture label (Rails’ posts(:first)).
fixture_id
The id of the fixture labelled label (Rails’ users(:david).id): the loader of tests/fixtures/*.yml gives a record without an explicit id the CRC-32 of its label modulo 2^30 - 1, like Rails.
freeze_time
Stops crate::now at the current second (Rails’ freeze_time); returns it.
insert
Inserts a row into table of the test database and returns its id: what generated factories (tests/factories/) call. One wrangler call (about a second).
quote
value as a SQL literal: strings quoted (' doubled), numbers as is, booleans as 1/0, null as NULL, arrays and objects as JSON text.
redact
Replaces values that change on every run by placeholders, so a snapshot or an assert_eq! on a whole page or JSON body is stable (Loco’s cleanup_* filters): UUIDs become <UUID>, ISO 8601 dates and times (2026-01-01, 2026-01-01T12:00:00Z, 2026-01-01 12:00:00) become <DATE>, and runs of 32 or more hexadecimal or base64url characters (tokens, digests) become <TOKEN>.
sequence
A unique number per call in the test process, for unique test data (FactoryBot’s sequence): 1, 2, 3…
sql
Rows returned by query, run on the test database (Rails’ ActiveRecord::Base.connection.select_all).
travel
Moves crate::now by seconds from its current value and freezes it there (Rails’ travel 1.day).
travel_back
Returns crate::now to the real clock (Rails’ travel_back).
travel_to
Makes crate::now return unix on this thread until travel_back (Rails’ travel_to, frozen). Each Rust test runs on its own thread, so other tests keep the real clock. Affects code running in the test process (models, helpers, JWT), not the cf dev server.
var
A variable of the app under test: the environment variable name, else its value in .dev.vars (the file ocre dev loads), as the Worker sees it. Request tests use it for the secrets they sign with (a webhook’s).