vtz runtime includes a built-in test runner powered by V8. No extra packages to install, no configuration required.
@vertz/test is a synthetic module provided automatically by the vtz runtime — like
node:fs in Node.js. You do not need to run vtz add @vertz/test.Quick start
Create a test file:**/*.test.ts and **/*.test.tsx files automatically.
Writing tests
Structure
Usedescribe to group tests and it (or test) to define individual test cases:
Hooks
beforeEach(fn)— runs before each test in the currentdescribeblockafterEach(fn)— runs after each testbeforeAll(fn)— runs once before all tests in the blockafterAll(fn)— runs once after all tests in the block
beforeEach runs before child beforeEach.
Modifiers
Conditional skip
Parameterized tests
Assertions
Theexpect API is compatible with Vitest. Here are the most common matchers:
Asymmetric matchers
Use insidetoEqual, toHaveBeenCalledWith, and other deep-equality matchers:
expect.any(constructor), expect.anything(), expect.objectContaining(), expect.arrayContaining(), expect.stringContaining(), expect.stringMatching().
Mocking
Mock functions
mockReturnValue, mockReturnValueOnce, mockResolvedValue, mockResolvedValueOnce, mockRejectedValue, mockRejectedValueOnce, mockImplementation, mockImplementationOnce.
Inspect calls via fn.mock.calls, fn.mock.results, and fn.mock.lastCall.
Clean up with mockClear() (reset call history), mockReset() (clear + reset implementation), or mockRestore() (restore original for spies).
Spying on methods
vi namespace
For Vitest compatibility, thevi object provides the same utilities:
Fake timers
useFakeTimers, useRealTimers, advanceTimersByTime, advanceTimersToNextTimer, runAllTimers, runOnlyPendingTimers, setSystemTime, getTimerCount, isFakeTimers.
Configuration
Configure the test runner invertz.config.ts:
vtz test uses sensible defaults.
CLI reference
| Option | Description |
|---|---|
[PATH...] | Specific files or directories to test (default: project root) |
--filter <str> | Filter tests by name substring |
--watch | Re-run tests when files change |
--coverage | Collect V8 code coverage (outputs coverage.lcov) |
--coverage-threshold <n> | Minimum coverage percentage as integer (default: 95) |
--timeout <ms> | Per-test timeout in milliseconds (default: 5000) |
--concurrency <n> | Max parallel test files (default: CPU count) |
--reporter <fmt> | Output format: terminal, json, or junit |
--bail | Stop after the first failure |
--no-preload | Skip preload scripts from config |
--no-cache | Skip compilation cache |
--root-dir <path> | Workspace root for module resolution |
Examples
Coming from Vitest or Jest
The@vertz/test API is intentionally compatible with Vitest. Your test logic stays the same — only the import path and runner change.
| Vitest | Vertz | |
|---|---|---|
| Import | import { describe, it, expect } from 'vitest' | import { describe, it, expect } from '@vertz/test' |
| Config | vitest.config.ts | vertz.config.ts |
| Run | npx vitest | vtz test |
| Install | npm add -D vitest | Nothing — built into the runtime |
Key differences
- No package to install.
@vertz/testis provided by thevtzruntime automatically. - Runs in V8, not Node.js. The test runner uses the same V8 engine as
vtz dev. - Snapshot testing (
toMatchSnapshot,toMatchInlineSnapshot) is not currently supported.
Migrating from bun:test
Thevtz migrate-tests command rewrites imports from bun:test to @vertz/test and adjusts API differences:
Troubleshooting
“Cannot find module ‘@vertz/test’” — You’re running tests with a different runner (Node, Bun, Vitest). Usevtz test instead — @vertz/test is only available inside the vtz runtime.
Coverage output — vtz test --coverage generates a coverage.lcov file in the project root. Use any LCOV-compatible viewer to inspect results.
Next steps
Server Testing
Type-safe test client for entity CRUD, service actions, and raw HTTP.
E2E Testing
Browser-based tests with Playwright and authenticated users.