@molecule/api-testing
Utility · testing · API (Node) · v1.0.2 · Apache-2.0
Test utilities and helpers
npm install @molecule/api-testingHow it works
@molecule/api-testing is a utility package for the API (Node) side (testing).
import { setPool } from '@molecule/api-database'
import { setTransport } from '@molecule/api-emails'
import { createMockDatabase, createMockEmail, createUserFixture } from '@molecule/api-testing'
// Fresh mocks per test file, wired exactly like a real bond — the code
// under test needs zero changes.
const db = createMockDatabase()
const email = createMockEmail()
setPool(db)
setTransport(email)
// Queue per-query results (FIFO, one per query), then a persistent fallback.
db.setQueryResultOnce({ rows: [createUserFixture()], rowCount: 1 })
db.setQueryResult({ rows: [], rowCount: 0 })
// ...run the code under test, then assert on what it did:
console.log(db.queries) // every { text, values } issued
console.log(email.sentMessages) // every EmailMessage sent
email.failNext(new Error('SMTP down')) // next sendMail() rejects onceReference
Testing utilities for molecule.dev API packages.
Provides in-memory mock implementations for five core interfaces —
database (DatabasePool), cache, queue, emails, logger — plus generic
async test helpers (waitFor, createDeferred, expectThrows,
createSpy, randomString/randomEmail/randomUUID) and
user/device/session fixture factories (createUserFixture,
createDeviceFixture, createSessionFixture, createMany).
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setPool } from '@molecule/api-database'
import { setTransport } from '@molecule/api-emails'
import { createMockDatabase, createMockEmail, createUserFixture } from '@molecule/api-testing'
// Fresh mocks per test file, wired exactly like a real bond — the code
// under test needs zero changes.
const db = createMockDatabase()
const email = createMockEmail()
setPool(db)
setTransport(email)
// Queue per-query results (FIFO, one per query), then a persistent fallback.
db.setQueryResultOnce({ rows: [createUserFixture()], rowCount: 1 })
db.setQueryResult({ rows: [], rowCount: 0 })
// ...run the code under test, then assert on what it did:
console.log(db.queries) // every { text, values } issued
console.log(email.sentMessages) // every EmailMessage sent
email.failNext(new Error('SMTP down')) // next sendMail() rejects once
Type
utility
Installation
npm install @molecule/api-testing @molecule/api-cache @molecule/api-database @molecule/api-emails @molecule/api-logger @molecule/api-queue
API
Interfaces
UserFixture
User fixture properties.
interface UserFixture {
id: string
username: string
email: string | null
name: string | null
createdAt: string
updatedAt: string
}
UserFixtureOverrides
User fixture override options.
interface UserFixtureOverrides {
id?: string
username?: string
email?: string
name?: string
createdAt?: string
updatedAt?: string
}
DeviceFixture
Device fixture properties.
interface DeviceFixture {
id: string
userId: string
platform: string
createdAt: string
updatedAt: string
}
DeviceFixtureOverrides
Device fixture override options.
interface DeviceFixtureOverrides {
id?: string
userId?: string
platform?: string
createdAt?: string
updatedAt?: string
}
SessionFixture
Session fixture properties.
interface SessionFixture {
userId: string
deviceId: string
token: string
expiresAt: string
}
SessionFixtureOverrides
Session fixture override options.
interface SessionFixtureOverrides {
userId?: string
deviceId?: string
token?: string
expiresAt?: string
}
WaitForOptions
Options for waitFor utility.
interface WaitForOptions {
timeout?: number
interval?: number
}
Deferred
Deferred promise structure.
interface Deferred<T> {
promise: Promise<T>
resolve: (value: T) => void
reject: (error: Error) => void
}
SpyCall
Spy call record.
interface SpyCall<TArgs extends unknown[], TResult> {
args: TArgs
result: TResult
}
Spy
Spy function with call tracking, count, and reset capabilities.
interface Spy<T extends (...args: unknown[]) => unknown> {
(...args: Parameters<T>): ReturnType<T>
calls: Array<SpyCall<Parameters<T>, ReturnType<T>>>
callCount: number
reset: () => void
}
LogEntry
Log entry captured by the mock logger.
interface LogEntry {
level: 'trace' | 'debug' | 'info' | 'warn' | 'error'
message: string
args: unknown[]
timestamp: Date
}
Functions
createUserFixture(overrides)
Creates a user fixture with random defaults, optionally overridden.
function createUserFixture(overrides?: Partial<UserFixtureOverrides>): UserFixture
overrides— Partial overrides for user properties (id, username, email, name, timestamps).
Returns: A complete UserFixture object.
createDeviceFixture(overrides)
Creates a device fixture with random defaults, optionally overridden.
function createDeviceFixture(overrides?: Partial<DeviceFixtureOverrides>): DeviceFixture
overrides— Partial overrides for device properties (id, userId, platform, timestamps).
Returns: A complete DeviceFixture object.
createSessionFixture(overrides)
Creates a session fixture with random defaults and a 7-day expiry, optionally overridden.
function createSessionFixture(overrides?: Partial<SessionFixtureOverrides>): SessionFixture
overrides— Partial overrides for session properties (userId, deviceId, token, expiresAt).
Returns: A complete SessionFixture object.
createMany(factory, count)
Creates an array of fixtures by calling a factory function count times.
function createMany(factory: (index: number) => T, count: number): T[]
factory— A function that receives the index and returns a fixture.count— Number of fixtures to create.
Returns: An array of count fixture objects.
wait(ms)
Waits for a specified number of milliseconds.
function wait(ms: number): Promise<void>
ms— Milliseconds to wait.
Returns: A promise that resolves after the delay.
waitFor(condition, options)
Polls a condition function until it returns true, or throws after the timeout.
The condition is always checked one final time after the deadline passes, so a condition that becomes true during the last polling interval still resolves instead of being falsely reported as timed out.
function waitFor(
condition: () => boolean | Promise<boolean>,
options?: WaitForOptions,
): Promise<void>
condition— A function that returnstrue(or a Promise resolving totrue) when the condition is met.options— Timeout and polling interval configuration.
createDeferred()
Creates a deferred promise that can be resolved or rejected externally.
function createDeferred(): Deferred<T>
Returns: An object with promise, resolve, and reject properties.
expectThrows(fn, errorType)
Runs a function and asserts it throws. Optionally checks the error type.
function expectThrows(
fn: () => Promise<unknown> | unknown,
errorType?: new (...args: unknown[]) => T,
): Promise<T>
fn— The function expected to throw (sync or async).errorType— Optional error constructor to assert against.
Returns: The caught error instance.
createSpy(implementation)
Creates a spy function that records all calls with arguments and return values.
The call is recorded even when the implementation throws (with result
left undefined), so callCount reflects "the spy was invoked", not
"the implementation returned" — callers can tell "called and threw"
apart from "never called".
function createSpy(implementation?: T): Spy<T>
implementation— Optional real implementation to delegate to.
Returns: A spy function with calls, callCount, and reset() properties.
randomString(length)
Generates a random alphanumeric string.
function randomString(length?: number): string
length— Character count (default 10).
Returns: A random string of the specified length.
randomEmail()
Generates a random email address at test.molecule.dev.
function randomEmail(): string
Returns: A random email string.
randomUUID()
Generates a random UUID v4 via crypto.randomUUID().
function randomUUID(): string
Returns: A random UUID string.
createMockCache()
Creates a mock cache provider for testing.
function createMockCache(): CacheProvider & {
store: Map<string, { value: unknown; tags?: string[] }>
reset: () => void
}
Returns: The created instance.
createMockDatabase()
Creates a mock database pool for testing.
function createMockDatabase(): DatabasePool & {
queries: Array<{ text: string; values?: unknown[] }>
setQueryResult: <T>(result: QueryResult<T>) => void
setQueryResultOnce: <T>(result: QueryResult<T>) => void
reset: () => void
}
Returns: The created instance.
createMockEmail()
Creates a mock email transport for testing.
function createMockEmail(): EmailTransport & {
sentMessages: EmailMessage[]
reset: () => void
failNext: (error: Error) => void
}
Returns: The created instance.
createMockLogger()
Creates a mock Logger that captures all log entries for assertions.
function createMockLogger(): Logger & {
logs: LogEntry[]
reset: () => void
getLogsByLevel: (level: LogEntry['level']) => LogEntry[]
setLevel: (level: string) => void
getLevel: () => string
}
Returns: A Logger with exposed logs array, reset(), and getLogsByLevel() for test inspection.
createMockQueue()
Creates a mock QueueProvider for testing, with lazily-created in-memory queues and a reset() method.
function createMockQueue(): QueueProvider & {
queues: Map<string, ReturnType<typeof createMockQueueInstance>>
reset: () => void
}
Returns: A QueueProvider with exposed queues map and reset() for test cleanup.
Constants
mockCache
Pre-configured mock cache for quick setup. Shared module-level instance —
call reset() in beforeEach so stored entries don't bleed between tests.
const mockCache: CacheProvider & {
store: Map<string, { value: unknown; tags?: string[] }>
reset: () => void
}
mockDatabase
Pre-configured mock database for quick setup. Shared module-level instance —
call reset() in beforeEach so recorded queries don't bleed between tests.
const mockDatabase: DatabasePool & {
queries: Array<{ text: string; values?: unknown[] }>
setQueryResult: <T>(result: QueryResult<T>) => void
setQueryResultOnce: <T>(result: QueryResult<T>) => void
reset: () => void
}
mockEmail
Pre-configured mock email for quick setup. Shared module-level instance —
call reset() in beforeEach so sent messages don't bleed between tests.
const mockEmail: EmailTransport & {
sentMessages: EmailMessage[]
reset: () => void
failNext: (error: Error) => void
}
mockLogger
Pre-configured mock logger instance for quick test setup. Shared
module-level instance — call reset() in beforeEach so captured log
entries don't bleed between tests.
const mockLogger: Logger & {
logs: LogEntry[]
reset: () => void
getLogsByLevel: (level: LogEntry['level']) => LogEntry[]
setLevel: (level: string) => void
getLevel: () => string
}
mockQueue
Pre-configured mock queue provider instance for quick test setup. Shared
module-level instance — call reset() in beforeEach so queued messages
and subscribers don't bleed between tests.
const mockQueue: QueueProvider & {
queues: Map<string, ReturnType<typeof createMockQueueInstance>>
reset: () => void
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-database^1.0.1@molecule/api-cache^1.0.1@molecule/api-queue^1.0.1@molecule/api-emails^1.0.1@molecule/api-logger^1.0.1
Runtime Dependencies
-
@molecule/api-cache -
@molecule/api-database -
@molecule/api-emails -
@molecule/api-logger -
@molecule/api-queue -
Wire mocks through each core's OWN setter —
setPool(@molecule/api-database),setTransport(@molecule/api-emails),setProvider(@molecule/api-cache,@molecule/api-queue),setLogger(@molecule/api-logger). Mocks exist ONLY for those five cores; for any other core, mock its bond with your test runner (e.g.vi.mock). -
createMockDatabase().setQueryResult(result)is persistent — every subsequentquery()call (pool, connection, or transaction) returns that same result until you callsetQueryResultagain orreset(). Despite the name's resemblance to vitest'smockResolvedValueOnce, it is NOT a one-shot queue. A handler test that issues multiple distinct queries and needs each to see a different result must usesetQueryResultOnce(result)instead — queued once-results are consumed first, in FIFO order, one per query, before queries fall back to the persistentsetQueryResultvalue (or the empty{ rows: [], rowCount: 0 }default). -
The prebuilt
mockDatabase/mockCache/mockQueue/mockEmail/mockLoggerconstants are shared module-level singletons — recorded queries, sent messages, and cache entries BLEED between test files that import them. Callreset()inbeforeEach, or prefer thecreateMockX()factories for per-file isolation. -
Name collisions:
waitForalso exists as vitest'svi.waitForand in@testing-library;createSpyoverlapsvi.fn. Import from ONE source per file so the wrong signature isn't picked up silently.