uvu: The Fastest Node.js Test Runner for Small Projects

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 uvu

Basic 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.js

No 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)                 // falsy

You 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 test

When 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 suite for grouped tests with shared before/after hooks
  • No config files required — zero setup
  • Works with any assertion library including node:assert

Read more