SSR framework for Svelte 5 + Bun with islands-based selective hydration
On this page
Testing
Unit tests
Pure functions, stores, and any non-server logic test directly with bun test — no Mochi-specific setup:
// src/lib/slugify.test.ts
import { expect, test } from 'bun:test';
import { slugify } from './slugify';
test('lowercases and dasherizes', () => {
expect(slugify('Hello World')).toBe('hello-world');
});bun testFull-app tests
Tests that boot a real server with Mochi.serve() — to fetch a page, hit an API route, or assert on rendered HTML — must run one file per process. Mochi.serve() allows only one instance per process (it pins config on globalThis.__mochi_config__), so two server-booting test files in the same bun test run throw Mochi.serve() has already been called.
runTests solves this: it globs src/**/*.test.ts and runs each file in its own bun test process, parallelised across CPU cores. Add a small script to your package:
// scripts/run-tests.ts
#!/usr/bin/env bun
import { runTests } from 'mochi-framework';
await runTests();Point your test script at it:
// package.json
{
"scripts": {
"test": "bun scripts/run-tests.ts"
}
}bun run testEach file gets a fresh process, so every test can call Mochi.serve({ port: 0 }) without colliding:
// src/routes.test.ts
import { afterAll, beforeAll, expect, test } from 'bun:test';
import type { Server } from 'bun';
import { Mochi } from 'mochi-framework';
import { routes } from './routes';
let server: Server;
beforeAll(async () => {
server = await Mochi.serve({ port: 0, logger: { enabled: false }, routes });
});
afterAll(() => server.stop(true));
test('GET / renders', async () => {
const res = await fetch(`http://localhost:${server.port}/`);
expect(res.status).toBe(200);
});Options
runTests(options?) accepts:
dir— package root to scan and run tests from. Defaults to the current working directory.sequential— files (relative todir) that must run on their own, after the parallel batch — for tests that can’t share machine state with others:
await runTests({ sequential: ['src/liveReload.test.ts'] });runTests exits the process with code 1 if any file fails, so it drops straight into CI.