Testing
Test your Valet queries and mutations.
Valet tests should follow the same philosophy used in this repository: prefer integration tests that run real code paths with real dependencies.
Use Bun's test runner
All examples in this guide use bun:test:
import { describe, test, expect } from 'bun:test'Valet's own tests use Bun (bun:test) across the SDK, server integration, and e2e suites.
Integration-first testing
Favor tests that exercise real behavior end-to-end:
- Use a real local database (
:memory:is fast) - Use a real
SyncEngine - Execute real handlers via
.definition.handler - Mock only system boundaries you do not control (for example: WebSocket/network)
Avoid heavy mocks of ctx.db or query builders for confidence tests. Those are useful only for narrow, fast feedback loops.
Example: test a mutation with real local dependencies
// tests/todos.integration.test.ts
import { describe, test, expect, beforeEach, afterEach } from 'bun:test'
import {
LocalDatabase,
SyncEngine,
createLocalMutationContext,
type SyncSchema,
} from 'valet-dev/local'
import { create } from '../valet/todos'
const schema: SyncSchema = {
todos: {
fields: {
title: { type: 'string' },
completed: { type: 'boolean' },
userId: { type: 'string' },
},
},
}
describe('todos.create', () => {
let db: LocalDatabase
let syncEngine: SyncEngine
beforeEach(async () => {
db = new LocalDatabase()
await db.init(':memory:')
syncEngine = new SyncEngine({ db, schema })
await syncEngine.initializeTables()
})
afterEach(async () => {
await db.close()
})
test('inserts a todo into the real local database', async () => {
const { ctx } = createLocalMutationContext(syncEngine, { userId: 'user-1' })
// You can also use `writer` from createLocalMutationContext(...) to inspect tracked write operations.
const id = await create.definition.handler(ctx, { title: 'Buy milk' })
// Direct DB read is fine in tests to verify persisted state after handler execution.
const inserted = await db.get(id)
expect(inserted).not.toBeNull()
expect(inserted).toMatchObject({
title: 'Buy milk',
userId: 'user-1',
})
})
})When unit tests are acceptable
Unit tests can still help for very focused logic checks, but treat them as a fast complement, not your main confidence layer.
If you write unit tests:
- Keep them small and explicit about what they do not cover
- Use
bun:test(not Vitest) - Follow up with at least one integration test for the same behavior
Testing React code
For high confidence, test React behavior through real provider setup (local DB + sync + generated hooks).
If you mock anything, mock only boundaries like transport/network behavior, not the entire data stack.
Where to look for real examples
- SDK integration tests:
valet/src/local/database.test.ts - Sync engine tests:
valet/src/local/sync.test.ts - End-to-end tests:
e2e/src/functions.test.ts