Skip to content

Aver

Know your system works.

Every project of sufficient complexity eventually builds a domain language for its tests. Aver gives that language a home in the type system.

Without Aver — three test files, three vocabularies, same behavior:

// Unit test
test('create task', () => {
board.create('Fix bug')
expect(board.get('Fix bug').status).toBe('backlog')
})
// API test
test('create task', async () => {
await request(app).post('/tasks').send({ title: 'Fix bug' })
const res = await request(app).get('/tasks/Fix bug')
expect(res.body.status).toBe('backlog')
})
// Browser test
test('create task', async ({ page }) => {
await page.fill('[data-test=new-task]', 'Fix bug')
await page.click('[data-test=create]')
await expect(page.locator('[data-test=task-Fix-bug]')).toBeVisible()
})

With Aver — one test, every level:

const { test } = suite(taskBoard)
test('create a task in backlog', async ({ when, then }) => {
await when.createTask({ title: 'Fix bug' })
await then.taskInStatus({ title: 'Fix bug', status: 'backlog' })
})
✓ create a task in backlog [unit] 3ms
✓ create a task in backlog [http] 48ms
✓ create a task in backlog [playwright] 890ms

Working on business logic? aver run --adapter unit. Touching the API layer? --adapter http. Need full confidence before deploy? --adapter playwright. Same test, right feedback loop for the layer you’re in. How fast the unit adapter runs depends on your design — nullables and dependency injection eliminate IO entirely; a real database keeps fidelity at the cost of speed. Either way, it’s a fraction of the browser.


  • Right feedback loop, every layer — Same test runs at unit speed during development, HTTP for API contracts, Playwright for full confidence. Pick the level that matches the work.
  • Lock in what existsapprove() captures current behavior as a baseline. Refactor underneath with confidence.
  • Prove your system is observable — Declare expected telemetry alongside domain operations. Verify spans, attributes, and causal connections in the same test that checks behavior.
  • Zero runtime dependencies — Core has no deps. Add protocols as you need them.

Terminal window
npm install --save-dev @averspec/core vitest
npx aver init
npx aver run

Or follow a tutorial: legacy code, greenfield, or telemetry verification.


  • You only need unit tests for a pure function — plain Vitest is simpler
  • It’s a prototype or throwaway — the domain layer pays off over time, not day one
  • Trivial CRUD with no business rules — if the vocabulary mirrors the schema, there’s nothing to abstract

Read more about when to use Aver →


LanguagePackageInstall
TypeScript@averspec/corenpm install --save-dev @averspec/core
Pythonaverspecpip install averspec
Goaver-gogo get github.com/averspec/aver-go
RubyaverspecSource only — RubyGems coming soon
RustaverspecSource only — crates.io coming soon
KotlinaverspecSource only — Maven Central coming soon

All six implementations share the same behavioral contract model: domains, typed markers, multi-adapter execution, and cross-language telemetry contract verification. Read about how they were built →


Aver tests itself using the same domain-driven architecture it provides. See the test suite.

Why this exists · Architecture · Example app