JOLT DATA SDK GUIDE · 07
Test without a daemon
An App Definition provides an in-memory test interface with the same typed
Resources and Items as App.connect(). Tests can exercise application rules
without starting Jolt Console, approving a session, or waiting for a network.
Test one identity
Use App.test() for isolated application behavior. Each call starts with
fresh state, even when it uses the same identity string.
import { describe, expect, it } from "vitest";
import { State } from "jolt-sdk/data";
import { TaskList } from "./mutations";
describe("one identity", () => {
it("uses the normal typed Resource interface", async () => {
const app = TaskList.test({ identity: "alice.jolt" });
const created = await app.tasks.create({ title: "Write a test", done: false });
const updated = await created.update({ done: true });
expect(updated.value).toEqual({ title: "Write a test", done: true });
});
it("starts each test instance with fresh state", async () => {
const first = TaskList.test({ identity: "alice.jolt" });
const created = await first.tasks.create({ title: "Temporary", done: false });
const second = TaskList.test({ identity: "alice.jolt" });
expect((await second.tasks.get(created.ref)).state).toBe(State.Missing);
});
});Schemas, migrations, access-shaped methods, immutable Items, lifecycle states,
and conflict policies all remain active. Test through app.tasks or another
generated Resource instead of arranging data through a separate fixture API.
Test Alice and Bob
Use App.testWorld() when identities should share deterministic state. Views
from world.as(identity) represent application users in one synchronized test
world.
import { describe, expect, it } from "vitest";
import { Feed } from "./subscriptions";
describe("two identities", () => {
it("shares deterministic public data between Alice and Bob", async () => {
const world = Feed.testWorld();
const alice = world.as("alice.jolt");
const bob = world.as("bob.jolt");
const post = await alice.posts.create({
text: "Hello Bob!",
postedAt: new Date("2026-08-31T12:00:00.000Z"),
});
const fromAlice = await bob.posts.for("alice.jolt").get(post.ref);
expect(fromAlice.isPresent()).toBe(true);
if (!fromAlice.isPresent()) throw new Error("Expected Alice's post");
expect(fromAlice.value.text).toBe("Hello Bob!");
expect("update" in fromAlice).toBe(false);
});
});Bob can read Alice's post because the Collection declares
Read.AnyIdentity. His remote view remains read-only, exactly as it does in a
connected application.
Model separate installations only when needed
world.as(identity) is the simple choice for application journeys. It does
not model offline installations.
When a concurrency test genuinely needs independent copies, use
world.device(identity, name). Named devices keep separate histories until
the test calls world.sync(). The advanced Manual conflicts
guide shows that shape with a workstation and laptop.
Do not introduce devices and synchronization into ordinary component or domain tests. They exist to test concurrent-edit policy, not as general fixture setup.
Know what the test interface proves
The in-memory interface is good for:
- schema validation and migrations;
- Resource method availability and application access shape;
- Item create, read, update, replace, delete, and restore behavior;
- automatic and Manual conflict policies; and
- Data Subscription and Change Stream application logic.
It does not prove Jolt Console approval, daemon feature compatibility, capability enforcement or revocation, durable restart behavior, provider discovery, or real multi-node networking. Keep a small real-daemon test for those boundaries, as Jolt does for Chirp and Spoke.
Run the tests
The guide examples use Vitest:
yarn vitest run
No special Jolt test runner or fixture language is required.
Continue learning
- Return to Data SDK fundamentals.
- Test retained remote views with Data Subscriptions.
- Build the complete beginner Chirp app.