Multi-Adapter Testing
Run the same test against multiple implementations — in-memory, HTTP API, and browser UI. Define behavior once, verify it everywhere.
Setup
You need a domain and at least two adapters. This guide uses a task board example with three adapters.
Domain
// domains/task-board.ts
import { defineDomain, action, query, assertion } from '@averspec/core'
export const taskBoard = defineDomain({
name: 'task-board',
actions: {
createTask: action<{ title: string }>(),
moveTask: action<{ title: string; status: string }>(),
},
queries: {},
assertions: {
taskInStatus: assertion<{ title: string; status: string }>(),
},
})
Unit Adapter
Tests against in-memory objects. Runs in ~1ms.
// adapters/task-board.unit.ts
import { adapt, unit } from '@averspec/core'
import { expect } from 'vitest'
import { Board } from '../src/board'
import { taskBoard } from '../domains/task-board'
export const unitAdapter = adapt(taskBoard, {
protocol: unit(() => new Board()),
actions: {
createTask: async (board, { title }) => board.create(title),
moveTask: async (board, { title, status }) => board.move(title, status),
},
queries: {},
assertions: {
taskInStatus: async (board, { title, status }) => {
const task = board.details(title)
expect(task?.status).toBe(status)
},
},
})
HTTP Adapter
Tests against a REST API. Runs in ~10ms.
// adapters/task-board.http.ts
import { adapt } from '@averspec/core'
import { expect } from 'vitest'
import { http } from '@averspec/protocol-http'
import { taskBoard } from '../domains/task-board'
export const httpAdapter = adapt(taskBoard, {
protocol: http({ baseUrl: 'http://localhost:3000' }),
actions: {
createTask: async (ctx, { title }) => {
await ctx.post('/tasks', { title })
},
moveTask: async (ctx, { title, status }) => {
await ctx.patch(`/tasks/${encodeURIComponent(title)}`, { status })
},
},
queries: {},
assertions: {
taskInStatus: async (ctx, { title, status }) => {
const res = await ctx.get(`/tasks/${encodeURIComponent(title)}`)
const task = await res.json()
expect(task.status).toBe(status)
},
},
})
Playwright Adapter
Tests against a browser UI. Runs in ~300ms.
// adapters/task-board.playwright.ts
import { adapt } from '@averspec/core'
import { expect } from '@playwright/test'
import { playwright } from '@averspec/protocol-playwright'
import { taskBoard } from '../domains/task-board'
export const playwrightAdapter = adapt(taskBoard, {
protocol: playwright(),
actions: {
createTask: async (page, { title }) => {
await page.getByPlaceholder('Task title').fill(title)
await page.getByRole('button', { name: 'Add' }).click()
},
moveTask: async (page, { title, status }) => {
await page.getByTestId(`task-${title}`).dragTo(
page.getByTestId(`column-${status}`)
)
},
},
queries: {},
assertions: {
taskInStatus: async (page, { title, status }) => {
const column = page.getByTestId(`column-${status}`)
await expect(column.getByText(title)).toBeVisible()
},
},
})
Register All Adapters
// aver.config.ts
import { defineConfig } from '@averspec/core'
import { unitAdapter } from './adapters/task-board.unit'
import { httpAdapter } from './adapters/task-board.http'
import { playwrightAdapter } from './adapters/task-board.playwright'
export default defineConfig({
adapters: [unitAdapter, httpAdapter, playwrightAdapter],
})
Write Tests Once
The test file imports the domain, never the adapters:
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
setupFiles: ['./aver.config.ts'],
},
})
// tests/task-board.spec.ts
import { suite } from '@averspec/core'
import { taskBoard } from '../domains/task-board'
const { test } = suite(taskBoard)
test('create and move a task', async ({ act, assert }) => {
await act.createTask({ title: 'Fix login bug' })
await assert.taskInStatus({ title: 'Fix login bug', status: 'backlog' })
await act.moveTask({ title: 'Fix login bug', status: 'in-progress' })
await assert.taskInStatus({ title: 'Fix login bug', status: 'in-progress' })
})
Run
npx aver run
✓ tests/task-board.spec.ts
✓ create and move a task [unit] 1ms
✓ create and move a task [http] 14ms
✓ create and move a task [playwright] 312ms
One test, three adapters, three levels of confidence.
Filtering
Run a specific adapter:
npx aver run --adapter unit
npx aver run --adapter http
Run a specific domain:
npx aver run --domain task-board