Writing a module

Testing

Test a Frigear module with Vitest and test fixtures, including database and sign-in tests, then document and release it.

This page is for module authors: how to boot your module in a test, test routes that need a database or a signed-in user, and finish before a release. Tests run with Vitest and @nuxt/test-utils against test fixtures. A test fixture is a minimal Nuxt app under test/fixtures/ that installs your module from source, so a test can boot it.

Write a test

test/fixtures/basic/nuxt.config.ts
import MyModule from '../../../src/module'

export default defineNuxtConfig({
  modules: ['@nuxthub/core', MyModule],
  runtimeConfig: { session: { password: 'a-test-only-session-password-at-least-32-chars' } },
  hub: { db: { dialect: 'sqlite', driver: 'libsql', applyMigrationsDuringBuild: false } },
})
test/basic.test.ts
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { $fetch, setup } from '@nuxt/test-utils/e2e'

describe('my-module', async () => {
  await setup({ rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)) })

  it('renders', async () => {
    expect(await $fetch('/')).toContain('basic')
  })
})

Run it from the repository root, never from inside the package:

pnpm --filter @frigear-nu/my-module lint
pnpm --filter @frigear-nu/my-module test
Every test fixture is built and booted on CI, at 15 to 130 seconds each. Reuse an existing one of the package when you can, and put tests that need no Nuxt server under test/unit/.

Test with a database

A test fixture whose tests touch the database needs committed migrations for the merged schema. That is your tables, plus the columns every extending module adds.

Add the modules to the test fixture

List them in its nuxt.config.ts, with a test-only session password and a local libsql database.

Generate its migrations

Run the command in Generate test fixture migrations and commit the result.

Boot it with dev: true

Pass dev: true to setup(), so NuxtHub applies the committed migrations on startup. Delete only the test fixture's own .data folder before the suite and after it, so repeated runs do not fail on duplicate users.

Working examples are the admin-db, magic-link-db and verified-email-change test fixtures of packages/users, and auth-db of packages/auth.

Test signed-in routes

A module whose routes call authorize, requireAuth or requireUser lists @frigear-nu/auth in moduleDependencies and in package.json. requireUser also needs @frigear-nu/users. Roles for the test users come from a role provider in the fixture: see Access control.

Every test fixture that includes such a module needs the auth_user_tokens migration. A column added by an extending module without a matching migration makes every write to its table fail. The symptom is a missing session cookie, not disabled sign-in.
  • Registration. Email and password sign-up needs authentication.settings.password, which is on by default. It turns on the sign-in and registration APIs. settings.pages.register only adds the page.
  • Assertions. Check the registration status and response body before you check the session cookie.

Test API routes

apiClient({ cookie?, bearer? }) calls a route with a session cookie or a bearer token. expectApiError(promise, status, code?) checks an error response. Both come from @frigear-nu/module-kit/testing. HTTP routes has the conventions they check.

Document and release

  • Document it. Add the module page as Documentation describes, and a row in the root README.md package list.
  • Release it. Copy a sibling's .release-it.json, change the package name and tag, and add the package to release.yml. Publishing has the steps.
  • Check it. Run the checks in Conventions before you open a pull request.

Next steps

Copyright © 2026