Query Reference
@svelte-lean/query/testing
createTestClient(): a client with a virtual clock, a deterministic transport, typed mocks and a call log; the same typed handlers serve MSW; doubles for live sources and persistence stores.
Markdown version of this page, for assistants (Markdown for AI)
On this page
Import
import { createTestClient, reply, route, mswHandlers } from '@svelte-lean/query/testing';Usage
// src/lib/api/members.test.ts
import { createMutation, createQuery } from '@svelte-lean/query';
import { createTestClient, reply } from '@svelte-lean/query/testing';
import { expect, test } from 'vitest';
import { createMember, listMembers } from '$api/members';
test('a refused invite marks the field and refetches nothing', async () => {
const t = createTestClient();
t.mock(listMembers, { rows: [], total: 0, page: 1, perPage: 20, lastPage: 1 }, { latency: 50 });
t.mock(createMember, () => reply.validation({ email: 'taken' }));
const list = createQuery(listMembers, () => ({}));
const save = createMutation(createMember);
t.observe(() => list.data);
await t.advance(50);
await save
.run({ name: 'Mei', email: 'mei@harbor.studio', team: 'Design', role: 'member' })
.catch(() => {});
expect(save.error?.fields?.fields.email).toEqual(['taken']);
expect(t.calls(listMembers)).toHaveLength(1);
expect(t.findings()).toEqual([]);
t.dispose();
});Size
Production Vite builds of a small consumer entry, Svelte external. A marginal row is the bytes the entry adds over the row it names.
| Build | Over | gzip | brotli | Budget (gzip) |
|---|---|---|---|---|
testing (not shipped) | – | 18878 B | 17235 B | 19300 B |
Read from packages/query/artifacts/size.json. Every entry and its method: Performance.
Functions
Test clients, replies, MSW interop and doubles.
Exports
Runtime exports of the entry.
| Name | Type | Default | Description |
|---|---|---|---|
# createTestClient | (init?: TestClientInit) => TestClient | – | A fresh realm client per test; dispose it with using or t.dispose(). |
# mock | TestClient['mock'] | – | t.mock on the installed test client. |
# reply | object | – | Scripted answers: errors of each kind, raw bodies, meta, sequences, deferred and never. |
# route | (endpoint, handler, options?: MockOptions) => MswRoute | – | A typed handler for one endpoint, for mswHandlers() or t.mock(route). |
# mswHandlers | (http: MswHttpLike, base: string, routes: MswRoute[], options?: { profile? }) => unknown[] | – | MSW handlers built with the application's own msw http namespace; answers go through the profile. |
# prng | (seed: number) => () => number | – | A seeded random function (retry jitter in tests). |
# fakeSource | (options?: { ready?: 'auto' | 'manual' }) => FakeLiveSource | – | A LiveSource double: emit events, confirm subscriptions, report the connection. |
# memoryStore | () => PersistStore | – | A PersistStore in memory. |
# persistStoreSuite | (factory) => PersistStoreCase[] | – | The contract cases a PersistStore must pass; run them in any test runner. |
reply
Members of reply; a handler returns one instead of a value.
| Name | Type | Default | Description |
|---|---|---|---|
() => Reply | – | A network failure. | |
() => Reply | – | A timeout. | |
(status, init?: { message?, code?, retryAfter? }) => Reply | – | An HTTP error; 401 and 403 are forbidden, 422 validation. | |
(fields, message?) => Reply | – | A 422 with field messages typed by the mutation body. | |
(init?: { code?, message? }) => Reply | – | An application refusal inside a 200. | |
(status?: 401 | 403) => Reply | – | Access withdrawn. | |
() => Reply | – | An answer that fails its schema. | |
() => Reply | – | No answer at all. | |
(value, meta: ResponseMeta) => Reply | – | A value with response meta (server-driven freshness). | |
(status, body, headers?) => Reply | – | A raw body read through the profile. | |
(...steps) => handler | – | One step per call; the last repeats. | |
() => Deferred | – | A handler the test resolves or rejects by hand. |
Options
TestClientInit and the mock options.
TestClientInit
Every ClientInit option except transport, clock, environment and parts, plus these.
| Name | Type | Default | Description |
|---|---|---|---|
# parts | EnginePart[] | 'lazy' | allParts | Every engine part is included up front (no loading step on the virtual clock); 'lazy' tests the path where each part arrives after the engine first asks for it. |
# clock | 'virtual' | 'real' | 'virtual' | real turns latency into real time (demos). |
# start | number | Date.UTC(2026, 0, 1) | Virtual epoch. |
# seed | number | 1 | PRNG seed for retry jitter. |
# unhandled | 'error' | 'pending' | 'error' | What a request with no mock does. |
# visible | boolean | true | Initial visibility. |
# online | boolean | true | Initial network state. |
# profile | Profile | – | Wire mode: mocks answer server-shaped bodies through the profile. |
# ssr | boolean | – | Server mode: no realm client, inert scheduler, no retries. |
MockOptions
The third argument of t.mock() and route().
| Name | Type | Default | Description |
|---|---|---|---|
# latency | Duration | ((call) => Duration) | 0 | Virtual latency. |
# when | (params) => boolean | – | Only answer matching calls. |
# times | number | – | Then fall back to the previous mock. |
# ignoreAbort | boolean | – | Resolve even after abort (tests the sequence guard). |
Test client
TestClient extends Client with these members.
TestClient
Time moves only when the test moves it.
| Name | Type | Default | Description |
|---|---|---|---|
# mock | (endpoint, handler, options?) => void | – | Answer an endpoint: a value, a reply, or a function of the call; typed by the endpoint. |
# calls | (endpoint?) => Call[] | – | The call log, of one endpoint or all. |
# pending | () => PendingCall[] | – | Calls without an answer yet. |
# advance | (time: Duration) => Promise<void> | – | Move the virtual clock and settle what is due. |
# flush | () => Promise<void> | – | Settle what is due now. |
# now | () => number | – | The virtual time. |
# observe | (read: () => unknown) => () => void | – | Read a result in a reactive context, as a component would. |
# seedEntry | (endpoint, params, value, options?) => void | – | Put a value in the cache without a request. |
# hide | () => void | – | The document becomes hidden. |
# show | () => void | – | The document becomes visible. |
# disconnect | () => void | – | The network goes offline. |
# reconnect | () => void | – | The network comes back. |
# findings | () => Finding[] | – | Development findings; devtools() runs in every test client. |
# dispose | () => void | – | Stop the client. |
Call
One entry of the call log.
| Name | Type | Default | Description |
|---|---|---|---|
# params | P | – | The params or input. |
# cause | Cause | 'mutation' | – | Why it was sent. |
# priority | 'high' | 'auto' | 'low' | – | Fetch priority. |
# attempt | number | – | 1, then retries. |
# scope | Scope | – | The scope. |
# sentAt | number | – | Virtual send time. |
# settledAt | number | undefined | – | Virtual settle time. |
# outcome | 'pending' | 'success' | 'error' | 'aborted' | – | How it ended. |
# error | QueryError | undefined | – | The error, when it failed. |
Guide
How to use it, with examples: Testing. All entry points: Svelte Lean Query.