Seeders
Seeders populate your test database with predictable data before each test run. The Seeder base class from stratal/seeder runs inside the request-scoped container via TestingModule.seed(), giving each seeder access to dependency injection.
Creating a seeder
Section titled “Creating a seeder”Extend the Seeder base class and implement the run() method. Use constructor injection to access your services:
import { Seeder } from 'stratal/seeder'import { inject, Transient } from 'stratal/di'import type { DatabaseService } from '@stratal/framework/database'
@Transient()export class UserSeeder extends Seeder { constructor( @inject('Database') private readonly db: DatabaseService, ) { super() }
async run(): Promise<void> { await this.db.user.create({ data: { id: 'test-user-id', email: 'user@test.com', name: 'Test User', emailVerified: true, role: 'user', }, }) }}Running seeders
Section titled “Running seeders”Use TestingModule.seed() to run one or more seeders. Pass seeder class constructors (not instances):
import { Test, type TestingModule } from '@stratal/testing'import { UserSeeder } from './seeders/user.seeder'import { RoleSeeder } from './seeders/role.seeder'
const module = await Test.createTestingModule({ imports: [AppModule],}).compile()
// Run a single seederawait module.seed(UserSeeder)
// Run multiple seedersawait module.seed(UserSeeder, RoleSeeder)Each seeder is resolved from the request-scoped container, so it has access to all registered providers via dependency injection.
Calling other seeders
Section titled “Calling other seeders”Use this.call() inside a seeder to invoke another seeder:
import { Seeder } from 'stratal/seeder'import { Transient } from 'stratal/di'import { UserSeeder } from './user.seeder'import { RoleSeeder } from './role.seeder'
@Transient()export class DatabaseSeeder extends Seeder { async run(): Promise<void> { await this.call(UserSeeder) await this.call(RoleSeeder) }}Typical test pattern
Section titled “Typical test pattern”A common pattern is to truncate the database and re-seed before each test:
import { describe, beforeEach, afterAll, it } from 'vitest'import { Test, type TestingModule } from '@stratal/testing'import { UserSeeder } from './seeders/user.seeder'
describe('UsersController', () => { let module: TestingModule
beforeEach(async () => { module = await Test.createTestingModule({ imports: [AppModule], }).compile() await module.truncateDb() await module.seed(UserSeeder) })
afterAll(async () => { await module.close() })
it('returns the seeded user', async () => { await module.assertDatabaseHas('user', { id: 'test-user-id' }) })})Full example
Section titled “Full example”A more complete seeder that creates multiple related records:
import { Seeder } from 'stratal/seeder'import { inject, DI_TOKENS, Transient } from 'stratal/di'import type { DatabaseService } from '@stratal/framework/database'
export const ADMIN_USER_ID = 'admin-user-id'export const REGULAR_USER_ID = 'regular-user-id'
@Transient()export class UserSeeder extends Seeder { constructor( @inject(DI_TOKENS.Database) private readonly db: DatabaseService, ) { super() }
async run(): Promise<void> { const [admin, regular] = await Promise.all([ this.db.user.create({ data: { id: ADMIN_USER_ID, email: 'admin@test.com', name: 'Admin User', emailVerified: true, role: 'admin', }, }), this.db.user.create({ data: { id: REGULAR_USER_ID, email: 'user@test.com', name: 'Regular User', emailVerified: true, role: 'user', }, }), ])
await this.db.account.createMany({ data: [ { accountId: admin.id, providerId: 'credential', userId: admin.id, password: 'hashed-password', }, { accountId: regular.id, providerId: 'credential', userId: regular.id, password: 'hashed-password', }, ], }) }}Next steps
Section titled “Next steps”- Application Seeders for seeding your database via the Quarry CLI outside of tests.
- Factories for generating test data with Faker.js.
- Testing Module for the full
TestingModuleAPI. - Testing Overview for setting up your test environment.