Skip to main content

Testing

The package provides two testing helpers:

  • DrizzleTestModule registers a client under the same DI tokens as DrizzleModule.
  • createDrizzleMockClient() and createDrizzleRepositoryMock() create simple, typed object mocks.

Unit Tests

Use mocks when you want to isolate a service from SQL behavior.

import { Test } from '@nestjs/testing';
import {
DrizzleTestModule,
createDrizzleMockClient,
} from 'nest-drizzle-native';

const db = createDrizzleMockClient({
query: {
users: {
findMany: () => [{ id: 'user_1', name: 'Ada' }],
},
},
});

const moduleRef = await Test.createTestingModule({
imports: [DrizzleTestModule.forRoot({ client: db })],
providers: [UsersService],
}).compile();

This is useful for fast service tests, but it is intentionally shallow. It does not prove SQL syntax, schema mapping, driver behavior, or transaction behavior.

Good mock tests answer questions like:

  • did the service call the expected repository method?
  • did it map application input before persistence?
  • did it handle a repository error path?

Avoid using mocks to approve query builders, migrations, constraints, ordering, driver shutdown, or transaction boundaries. Those are database behaviors and need a real client somewhere in the test suite.

Repository Tests

Use repository mocks when a service depends on a repository and the repository has its own focused tests.

const usersRepository = createDrizzleRepositoryMock<UsersRepository>({
findById: async id => ({ id, name: 'Ada' }),
});

Repository implementations should usually get at least one real database test. The smallest useful pattern is:

  1. create a local disposable database file
  2. register it with DrizzleTestModule.forRoot()
  3. register repositories with DrizzleTestModule.forFeature()
  4. create tables or apply migrations
  5. assert inserts, updates, filters, ordering, and constraints with real rows

The 10-testing-utilities sample shows this split: service orchestration uses repository mocks, while the repository query shape uses a real libSQL database.

Integration Tests

Use a real Drizzle client when the behavior being tested depends on the database:

  • query shape and selected columns
  • migrations or table setup
  • inserts, updates, deletes, and ordering
  • transaction commit and rollback
  • driver-specific behavior

The package test suite includes real driver integration coverage for libSQL, better-sqlite3, PostgreSQL, and MySQL. The CI suite runs PostgreSQL and MySQL through service containers, while local runs skip those two unless connection URLs are provided.

The focused samples use the same philosophy: run real Nest modules against local database files when behavior depends on Drizzle, drivers, or transactions.

Local Real Database Tests

Use local file-backed databases for fast integration tests that should run on every machine and in every pull request. This is the right default for:

  • repository query behavior
  • migrations against SQLite-compatible schemas
  • basic module wiring with real clients
  • transaction commit and rollback behavior that does not depend on a specific networked driver

Prefer unique database files per test or per scenario. That keeps tests parallel-friendly and prevents previous runs from leaving hidden state behind.

Inspect:

Service-Backed Driver Tests

Use PostgreSQL, MySQL, or another service-backed driver when the behavior depends on that dialect or driver:

  • pool construction and shutdown
  • SQL dialect differences
  • generated column types and constraint behavior
  • driver-specific errors
  • production-like connection settings

Local runs should skip these tests when their URL is missing. CI should provide disposable services and run the real round trip before merge.

NEST_DRIZZLE_NATIVE_POSTGRES_URL=postgresql://user:password@127.0.0.1:5432/app \
npm run test --workspace nest-drizzle-native-sample-15-postgres-driver

NEST_DRIZZLE_NATIVE_MYSQL_URL=mysql://user:password@127.0.0.1:3306/app \
npm run test --workspace nest-drizzle-native-sample-16-mysql-driver

Use local-only credentials or disposable CI credentials for these commands. Never commit production database URLs or captured production data.

Inspect:

Transaction Behavior Tests

Transaction tests must assert database state before and after the call. A mock can say that a method was called; it cannot prove that a rollback removed a row or that transaction context was cleaned up.

Good transaction tests check:

  • state before the workflow starts
  • state after a committed workflow
  • state after a failed workflow
  • ledger/audit rows that should commit or roll back with the main write
  • TransactionHost.isTransactionActive() or equivalent context checks after the method returns

Use recreate-per-test databases first. Rollback-per-test fixtures can be useful later, but only after the application has enough test volume to justify the extra setup.

Choosing Test Depth

Test goalRecommended setupWhy
Provider wiring or service orchestrationDrizzleTestModule with createDrizzleMockClient() or repository mocksFast and focused on Nest collaboration, not SQL behavior
Query shape, ordering, constraints, or migrationsReal local Drizzle client with a local database fileProves the schema and SQL path without external services
Driver-specific behaviorReal service-backed driver such as PostgreSQL or MySQLCatches pool, dialect, and driver lifecycle behavior
Commit, rollback, and transaction context cleanupReal local database with recreate-per-test or rollback-per-test isolationMocks cannot prove transaction semantics

For transaction behavior, prefer a fresh local database per scenario until the application has enough test volume to justify a shared rollback fixture. The focused 19-transaction-isolation-testing sample demonstrates the recreate-per-test pattern with real @Transactional() calls.

Local Commands

npm run test
npm run test:cov
npm run ci

npm run test:cov writes coverage/coverage-summary.json and test-results.json, which are used by the GitHub coverage and performance comments.