Rstest

Rstest is a testing framework developed by the Rspack team and built on top of Rspack for fast test execution.

This guide explains how to integrate Rstest with Modern.js for web app testing.

Quick Start

Install the base dependencies first:

npm
yarn
pnpm
bun
deno
npm add @rstest/core @modern-js/adapter-rstest -D

Then create rstest.config.ts:

rstest.config.ts
import { defineConfig } from '@rstest/core';
import { withModernConfig } from '@modern-js/adapter-rstest';

export default defineConfig({
  extends: withModernConfig(),
});

@modern-js/adapter-rstest lets Rstest inherit your existing Modern.js config so your test setup stays aligned with your app.

For more configuration details, refer to the Rstest configuration documentation.

You can run tests with npx rstest, or add a script in package.json:

package.json
{
  "scripts": {
    "test": "rstest"
  }
}

Testing Web UI

For web UI tests in Modern.js, there are two common approaches:

  • Use Rstest browser mode, which runs tests in a real browser. This is the recommended option in most cases, although it is currently experimental.
  • Use a simulated DOM environment with happy-dom and Testing Library. This matches the default web test environment provided by @modern-js/adapter-rstest.

We generally recommend browser mode because it uses real browser APIs and behavior, supports cases that simulated DOM environments cannot fully cover, and offers a better debugging experience when UI behavior does not match expectations.

Browser mode (experimental)

If you want to test in a real browser instead of a simulated DOM, install the browser mode dependencies:

npm
yarn
pnpm
bun
deno
npm add @rstest/browser @rstest/browser-react playwright -D

Then enable browser mode in rstest.config.ts:

rstest.config.ts
import { defineConfig } from '@rstest/core';
import { withModernConfig } from '@modern-js/adapter-rstest';

export default defineConfig({
  extends: withModernConfig(),
  browser: {
    enabled: true,
    provider: 'playwright',
  },
});

Example test:

__tests__/page.browser.test.tsx
import { BrowserRouter as Router } from '@modern-js/runtime/router';
import { page } from '@rstest/browser';
import { render } from '@rstest/browser-react';
import { expect, test } from '@rstest/core';
import Page from '../routes/page';

test('Page', async () => {
  await render(
    <Router>
      <Page />
    </Router>,
  );

  await expect.element(
    page.getByRole('heading', { level: 1, name: 'Home' }),
  ).toBeVisible();
});

For more browser mode setup, Locator APIs, and assertions, refer to the Rstest documentation.

DOM simulation with happy-dom

Install the DOM testing dependencies:

npm
yarn
pnpm
bun
deno
npm add happy-dom @testing-library/react @testing-library/dom -D

Update rstest.config.ts to use happy-dom:

rstest.config.ts
import { defineConfig } from '@rstest/core';
import { withModernConfig } from '@modern-js/adapter-rstest';

export default defineConfig({
  extends: withModernConfig(),
  testEnvironment: 'happy-dom',
});

First, create a simple page for testing:

routes/page.tsx
import { Link } from '@modern-js/runtime/router';

const Page = () => (
  <div>
    <h1>Home</h1>
    <Link to="/about">About</Link>
  </div>
);

export default Page;

Then add a test case:

__tests__/page.test.tsx
import { BrowserRouter as Router } from '@modern-js/runtime/router';
import { expect, test } from '@rstest/core';
import { render, screen } from '@testing-library/react';
import Page from '../routes/page';

test('Page', () => {
  render(
    <Router>
      <Page />
    </Router>,
  );

  expect(screen.getByRole('heading', { level: 1, name: 'Home' })).toBeDefined();
});

Running Test Cases

Execute the test command above to run your tests:

 __tests__/page.test.tsx (1)
 Page

 Test Files 1 passed
      Tests 1 passed
   Duration 510ms (build 145ms, tests 365ms)

Node Mode

If you need node mode tests for server-side logic such as bff, refer to the Rstest documentation directly.

Modern.js mainly targets web apps, so this guide focuses on web UI testing and browser mode.