uvu: The Fastest Node.js Test Runner for Small Projects
uvu is an extremely fast Node.js test runner with a minimal API and near-zero overhead. It runs tests as plain Node.js scripts — no CLI magic, no configuration files. If you need a lightweight alternative to Jest or Vitest for a library or small project, uvu is worth considering.
Installing uvu
npm install --save-dev uvuBasic Tests
import { test } from 'uvu';
import * as assert from 'uvu/assert';
test('adds two numbers', () => {
assert.is(1 + 1, 2);
});
test('deep equal objects', () => {
assert.equal({ a: 1 }, { a: 1 });
});
test.run();Run it:
node tests/math.test.jsNo test runner CLI needed — test.run() at the end of the file executes all registered tests.
Assertions
uvu provides its own uvu/assert module:
import * as assert from 'uvu/assert';
assert.is(actual, expected) // strict equality (===)
assert.is.not(actual, expected) // not strictly equal
assert.equal(actual, expected) // deep equality
assert.not.equal(actual, expected)
assert.type(value, 'string') // typeof check
assert.instance(value, Array) // instanceof check
assert.match(string, /pattern/) // regex match
assert.not.match(string, /pattern/)
assert.throws(fn, expected?) // throws synchronously
assert.unreachable('should not reach here')
assert.ok(value) // truthy
assert.not.ok(value) // falsyYou can also use Node's node:assert directly — uvu doesn't care which assertion library you use.
Test Suites
Group related tests with suite:
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
const UserService = suite('UserService');
UserService.before(async context => {
context.db = await connectDB(':memory:');
});
UserService.after(async context => {
await context.db.close();
});
UserService.before.each(async context => {
await context.db.seed();
});
UserService.after.each(async context => {
await context.db.truncate();
});
UserService('creates a user', async context => {
const user = await context.db.createUser({ name: 'Alice' });
assert.ok(user.id);
assert.is(user.name, 'Alice');
});
UserService('throws on duplicate email', async context => {
await context.db.createUser({ email: 'alice@example.com' });
assert.throws(
() => context.db.createUserSync({ email: 'alice@example.com' }),
/UNIQUE constraint/,
);
});
UserService.run();Async Tests
uvu handles promises automatically — just return a promise or use async/await:
import { test } from 'uvu';
import * as assert from 'uvu/assert';
test('fetches data', async () => {
const data = await fetchData();
assert.ok(data.length > 0);
});
test.run();Running Multiple Test Files
Use uvu's CLI to glob test files:
npx uvu tests '\.test\.js$'Or add it to package.json:
{
"scripts": {
"test": "uvu tests '\\.test\\.js$'"
}
}The second argument is a regex pattern for matching files within the directory.
ESM and CJS Support
uvu works with both ESM and CommonJS:
// ESM
import { test } from 'uvu';
import * as assert from 'uvu/assert';
// CommonJS
const { test } = require('uvu');
const assert = require('uvu/assert');Skipping Tests
test.skip('not ready yet', () => {
// This test is skipped
});Using Standard Library Assertions
uvu doesn't require its own assertion library. Node's assert works fine:
import { test } from 'uvu';
import assert from 'node:assert/strict';
test('works with node assert', () => {
assert.strictEqual(1 + 1, 2);
assert.deepStrictEqual([1, 2], [1, 2]);
});
test.run();Performance Comparison
uvu's speed advantage is most visible when you have many small tests. The overhead per-test is near zero because uvu doesn't spin up a worker for each file — tests run in the current process.
This means:
- Fast startup (~10ms vs ~1s for Jest)
- No parallelization (tests run serially)
- No process isolation between test files
For large test suites, the lack of parallelization becomes a disadvantage. For libraries with 20–200 tests, uvu's startup speed wins.
CI Integration
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm testWhen to Choose uvu
uvu is a good fit when:
- Building a library or utility package
- You want the fastest possible test startup
- You have a small to medium test suite (under ~500 tests)
- You prefer minimal, explicit setup over convention-heavy frameworks
Choose something else when:
- You need parallel file execution (use AVA or Vitest)
- You need module mocking (use Jest or Vitest)
- You're testing a browser environment (use Vitest + jsdom or Playwright)
- You need snapshot testing
Key Takeaways
- Tests are plain Node.js scripts — run them directly with
node test.run()at the end of each file triggers execution- Use
suitefor grouped tests with shared before/after hooks - No config files required — zero setup
- Works with any assertion library including
node:assert