Valet

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

On this page