kysely-vitest provides helpers to test your modules that depends on kysely to query your database.
kysely-vitest depends on both kysely@^0.28.0 and vitest@^4.0.0 as peer dependencies for all adapters. Run one of the following commands to install the dependencies:
npm install kysely && npm install -D vitest
# or
yarn add kysely && yarn add -D vitest
# or
pnpm add kysely && pnpm add -D vitestThen, you can define your database types in src/db.ts:
// in src/db.ts
export type DB = {
// ...
};Once these dependencies have been installed and your database types defined, you can install one of the adapters provided by kysely-vitest:
First, install the @kysely-vitest/postgres adapter
npm install kysely-postgres-js && npm install -D @kysely-vitest/postgres
# or
yarn add kysely-postgres-js && yarn add -D @kysely-vitest/postgres
# or
pnpm add kysely-postgres-js && pnpm add -D @kysely-vitest/postgresNote: The @kysely-vitest/postgres adapter uses the postgres.js. If you want to use the pg adapter, you can create your own dapater
First, we will register the kyselyPostgres plugin in vitest.config.ts:
// in vitest.config.ts
import path from "node:path";
import { kyselyPostgres } from "@kysely-vitest/postgres/plugin.js";
import { defineConfig } from "vitest/config";
import type { DB } from "./src/db.js";
export default defineConfig({
plugins: [
// Other plugins
kyselyPostgres<DB>({
config: {
database: "testdb",
username: "test",
password: "test",
},
migrationFolder: path.resolve(__dirname, "migrations"),
}),
],
test: {
// Test configuration
},
});Then we can create the pgTest function in src/tests/pgTest.ts that will be used in our test suite:
// in src/tests/pgTest.ts
import { createPostgresTestFunction } from "@kysely-vitest/postgres/test.js";
import type { DB } from "../db.js";
export const pgTest = createPostgresTestFunction<DB>();Note: The test function will create a database client by worker.
To run a test on your database, use the pgTest function instead of the it/test function provided by vitest. Note that pgTest is an extension of it/test and can be used exactly as the base function.
// in src/myTestSuite.spec.ts
import { describe, expect } from "vitest";
import { pgTest } from "./tests/pgTest.js";
describe("myTestSuite", () => {
pgTest("my test", async ({ db }) => {
// ...
});
});Note: The db parameter is a transaction that is rolled back after the test to ensure that each test runs in isolation.
To run you test suite with a managed container, you can configure the container to use in the vitest.config.ts file.
Here is an example on how to configure a managed container with @kysely-vitest/postgres:
// in vitest.config.ts
export default defineConfig({
plugins: [
// Other plugins
kyselyPostgres<DB>({
config: {
// Will be used as host port, defaults to `5432`
port: 5433,
// Will be used in POSTGRES_DB env, defaults to `testdb`
database: "mydb",
// Will be used in POSTGRES_USER env, defaults to `testuser`
user: "myuser",
// Will be used in POSTGRES_PASSWORD env, defaults to `test`
password: "secret",
// The container will use postgres:latest
dockerContainer: true
},
}),
],
});You can also select a custom image or tag for the container:
// in vitest.config.ts
export default defineConfig({
plugins: [
// Other plugins
kyselyPostgres<DB>({
config: {
// ... configuration
// Use custom image / tag
dockerContainer: {
image: "postgres",
tag: "18-alpine",
},
},
}),
],
});To run you test suite with an unmanaged container, you will need to start a PostgreSQL database on your own. The @kysely-vitest/postgres plugin will automatically run your migrations on that database.
You can use the following docker-compose.yml file to run your test database:
# in docker-compose.yml
services:
postgres:
image: postgres:18-alpine
environment:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 10s
timeout: 5s
retries: 5Then run your test suite with the following commands:
# Start test database
docker compose up --wait
# Run you tests
npx vitest
# or
yarn vitest
# or
pnpm vitest
# Stop test database an cleanup volumes
docker compose down --volumes@kysely-vitest/core provides helper functions to create you custom adapter for kysely-vitest. An adapter consists in 3 functions:
- A
kyselydialect factory - A
vitestplugin function - A
testfunction factory
The dialect factory will be used to initialize the connection to the database when running migrations, seeding the database and running tests.
Here is an example on how to create you dialect factory function:
// in src/tests/dialect.ts
import type { DialectFactory } from "@kysely-vitest/core/types.js";
import { MyDialect } from 'kysely/my-dialect';
export const MY_DIALECT_CONFIG_KEY = "myDialectConfig" as const;
export const myDialectFactory: DialectFactory<
typeof MY_DIALECT_CONFIG_KEY
> = (config) => {
return new MyDialect({
// Dialect configuration
})
};
// Extend vitest types
declare module "vitest" {
export type MyDialectConfig = {
// Dialect options
};
export interface ProvidedContext {
myDialectConfig: MyDialectConfig;
}
}Once your dialect function has been created, you can create the plugin function that will be used by vitest to run migrations, seed the database and configure your test function.
Here is an example on how to create your plugin:
// in src/tests/plugin.ts
import { createPlugin } from "@kysely-vitest/core/plugin.js";
import { MY_DIALECT_CONFIG_KEY, myDialectFactory } from "./dialect.js";
export const kyselyPlugin = createPlugin({
name: "plugin",
configKey: MY_DIALECT_CONFIG_KEY,
dialectFactory: myDialectFactory,
});Then, you can use your plugin in the vitest.config.ts file:
// in vitest.config.ts
import path from "node:path";
import { defineConfig } from "vitest/config";
import type { DB } from "./src/db.js";
import { kyselyPlugin } from "./src/tests/plugin.js";
export default defineConfig({
plugins: [
// Other plugins
kyselyPlugin<DB>({
config: {
// Your dialect configuration
},
migrationFolder: path.resolve(__dirname, "migrations"),
}),
],
test: {
// Test configuration
},
});Note: You can use also use a seed function with your plugin.
The configProvider function can be used to cleanup the configuration that will be passed to the test context and configure the managed docker container.
Pass your custom config provider when you configure your plugin:
// ...
import { configProvider, type MyConfig } from "./config.js";
export const kyselyPlugin = createPlugin<
typeof MY_DIALECT_CONFIG_KEY,
MyConfig
>({
name: "plugin",
configKey: MY_DIALECT_CONFIG_KEY,
configProvider,
dialectFactory: myDialectFactory,
});Then you can create your custom config provider in src/tests/config.js:
// in src/tests/config.js
export const configProvider: ConfigProvider<
typeof MY_DIALECT_CONFIG_KEY,
MyConfig
> = async (config) {
return {
config: {
// ... transform provided config
},
dockerContainer: {
// ... Docker container configuration
} | false
}
}Once your plugin has been configured, you can create your test function. Here is an example of a dbTest function:
// in src/tests/dbTest.ts
import { createTestFunction } from "@kysely-vitest/core/test.js";
import { MY_DIALECT_CONFIG_KEY, myDialectFactory } from "./dialect.js";
import type { DB } from "../db.js";
export const dbTest = createTestFunction<typeof MY_DIALECT_CONFIG_KEY, DB>({
configKey: MY_DIALECT_CONFIG_KEY,
dialectFactory: myDialectFactory,
});Then you can use your test function inside your test suites:
// in src/myTestSuite.spec.ts
import { describe, expect } from "vitest";
import { dbTest } from "./tests/dbTest.js";
describe("myTestSuite", () => {
dbTest("my test", async ({ db }) => {
// ...
});
});Note: The db parameter is a transaction that is rolled back after the test to ensure that each test runs in isolation.
You can provide a seeding function to your adapter to seed your database before running the tests. Here is an example with the @kysely-vitest/postgres adapter:
First create your seed function in src/tests/seed.js:
// in src/tests/seed.js
import type { SeedFunction } from "@kysely-vitest/postgres/types.js";
import { sql } from "kysely";
import type { DB } from "../db.js";
export const seed: SeedFunction<DB> = async (db) => {
await sql<void>`TRUNCATE TABLE "users" RESTART IDENTITY CASCADE`.execute(db);
await db
.insertInto("users")
.values([{ username: "alice" }, { username: "bob" }])
.execute();
};Then configure the plugin in vitest.config.ts to use your seed function:
// in vitest.config.ts
import path from "node:path";
import { kyselyPostgres } from "@kysely-vitest/postgres/plugin.js";
import { defineConfig } from "vitest/config";
import type { DB } from "./src/db.js";
import { seed } from "./src/tests/seed.js";
export default defineConfig({
plugins: [
// Other plugins
kyselyPostgres<DB>({
// Configure the plugin
seed,
}),
],
test: {
// Test configuration
},
});Note: The seeding function will be run once before running your tests suite and will not run on test reload.