Mocking Drizzle instance

Hey there, I am currently trying to find a good way to mock our drizzle instance for our unit test suite, but the nature of the usage of it makes it kinda hard. Is there somebody who is running a code base with a mocked drizzle?
14 Replies
fermentfan
fermentfanOP•16mo ago
@bloberenober @Andrew Sherman Any idea? 😦
Andrii Sherman
Andrii Sherman•16mo ago
Sorry for a late response, finally have free time to answer all questions in Discord https://discord.com/channels/1043890932593987624/1101108758127591435/1139240309939777719 I'll answer this question anyway We don't have mocks for drizzle. How do you want to use it? And why do you need to mock database for tests at all?
andrevandal
andrevandal•14mo ago
hey @Andrew Sherman I have the same scenario here, I'm testing my repositories and I'm creating an unit test and I need to mock it hehe
Dan
Dan•14mo ago
why not use something like sinon to stub/mock specific methods on the db? or if you are using vitest it has its own mocking API I think there's nothing specific to Drizzle that can't be mocked like anything else
andrevandal
andrevandal•14mo ago
I've tried to mock using vitest but I'm facing some errors have you already use vitest to mock drizzle?
jakeleventhal
jakeleventhal•11mo ago
// drizzle.mock.ts
import { DeepMockProxy, mockDeep, mockReset } from 'jest-mock-extended';

import { db } from '../src';

const drizzleMock: DeepMockProxy<typeof db> = mockDeep();

jest.mock('../src/index', () => ({
__esModule: true,
...jest.requireActual('../src/index'),
default: drizzleMock,
}));

beforeEach(() => {
mockReset(drizzleMock);
});

export default drizzleMock;
// drizzle.mock.ts
import { DeepMockProxy, mockDeep, mockReset } from 'jest-mock-extended';

import { db } from '../src';

const drizzleMock: DeepMockProxy<typeof db> = mockDeep();

jest.mock('../src/index', () => ({
__esModule: true,
...jest.requireActual('../src/index'),
default: drizzleMock,
}));

beforeEach(() => {
mockReset(drizzleMock);
});

export default drizzleMock;
Then add this to your jest config:
setupFilesAfterEnv: ['./path./to/drizzle.mock.ts']
setupFilesAfterEnv: ['./path./to/drizzle.mock.ts']
And here is a sample test
import drizzleMock from '../drizzle.mock';
import { NextApiRequest, NextApiResponse } from 'next';
import { createMocks } from 'node-mocks-http';

import someEndpoint from './get';

describe('/api/app-data/get', () => {
const data: AppData = { id: '123', maintenance: true };

it('should throws an error if no app data exists', async () => {
const { req, res } = createMocks({
method: 'GET'
});

await someEndpoint(req as unknown as NextApiRequest, res as unknown as NextApiResponse);

expect(drizzleMock.query.someTable.findFirst).toHaveBeenCalledTimes(1);
expect(res._getStatusCode()).toBe(500);
});

it('should return the app data when drizzle succeeds', async () => {
drizzleMock.query.someTable.findFirst.mockResolvedValueOnce(data);

const { req, res } = createMocks({
method: 'GET'
});
await someEndpoint(req as unknown as NextApiRequest, res as unknown as NextApiResponse);

expect(drizzleMock.query.someTable.findFirst).toHaveBeenCalledTimes(1);
expect(res._getStatusCode()).toBe(200);
expect(res._getData()).toStrictEqual(data);
});
});
import drizzleMock from '../drizzle.mock';
import { NextApiRequest, NextApiResponse } from 'next';
import { createMocks } from 'node-mocks-http';

import someEndpoint from './get';

describe('/api/app-data/get', () => {
const data: AppData = { id: '123', maintenance: true };

it('should throws an error if no app data exists', async () => {
const { req, res } = createMocks({
method: 'GET'
});

await someEndpoint(req as unknown as NextApiRequest, res as unknown as NextApiResponse);

expect(drizzleMock.query.someTable.findFirst).toHaveBeenCalledTimes(1);
expect(res._getStatusCode()).toBe(500);
});

it('should return the app data when drizzle succeeds', async () => {
drizzleMock.query.someTable.findFirst.mockResolvedValueOnce(data);

const { req, res } = createMocks({
method: 'GET'
});
await someEndpoint(req as unknown as NextApiRequest, res as unknown as NextApiResponse);

expect(drizzleMock.query.someTable.findFirst).toHaveBeenCalledTimes(1);
expect(res._getStatusCode()).toBe(200);
expect(res._getData()).toStrictEqual(data);
});
});
jakeleventhal
jakeleventhal•11mo ago
youre welcome
No description
collinbxx
collinbxx•11mo ago
@jakeleventhal How can I mock with select, insert and update?
jakeleventhal
jakeleventhal•11mo ago
what i eneded up doing is basically using very few of those drizzle mock tests IMO, any test like that is kind of weak. its much better to either do an integration test or dont make those checks entirely
export default async () => {
const { container } = await SetupDatabase.start());

try {
// <TEST CODE>
} finally {
await container.stop();
}
};
export default async () => {
const { container } = await SetupDatabase.start());

try {
// <TEST CODE>
} finally {
await container.stop();
}
};
import { PostgreSqlContainer } from '@testcontainers/postgresql';
import { exec as execCb } from 'child_process';
import { ImportMock } from 'ts-mock-imports';
import { promisify } from 'util';

const exec = promisify(execCb);

export default class SetupDatabase {
private static startTestContainer = async (dbMount: string | undefined) => {
const container = await new PostgreSqlContainer('postgres:15.0')
.withUsername('postgres')
.withPassword('local')
.withDatabase('postgres')
.withBindMounts(
dbMount
? [
{
source: dbMount,
target: '/var/lib/postgresql/data'
}
]
: []
)
.start();

return container;
};

private static pushDatabaseSchema = async (url: string) => {
await exec(`npx drizzle-kit push:pg --config=../../../drizzle.config.ts`, {
env: {
...process.env,
DATABASE_URL: url,
}
});
};

static start = async (dbMount?: string) => {
// Start the container and get the database URL
const container = await this.startTestContainer(dbMount);
const url = container.getConnectionUri();

// Push the Prisma database schema
await this.pushDatabaseSchema(url);

ImportMock.mockOther(databasePackage, 'default', databasePackage.getDrizzleClient(url));

return { container};
};
}
import { PostgreSqlContainer } from '@testcontainers/postgresql';
import { exec as execCb } from 'child_process';
import { ImportMock } from 'ts-mock-imports';
import { promisify } from 'util';

const exec = promisify(execCb);

export default class SetupDatabase {
private static startTestContainer = async (dbMount: string | undefined) => {
const container = await new PostgreSqlContainer('postgres:15.0')
.withUsername('postgres')
.withPassword('local')
.withDatabase('postgres')
.withBindMounts(
dbMount
? [
{
source: dbMount,
target: '/var/lib/postgresql/data'
}
]
: []
)
.start();

return container;
};

private static pushDatabaseSchema = async (url: string) => {
await exec(`npx drizzle-kit push:pg --config=../../../drizzle.config.ts`, {
env: {
...process.env,
DATABASE_URL: url,
}
});
};

static start = async (dbMount?: string) => {
// Start the container and get the database URL
const container = await this.startTestContainer(dbMount);
const url = container.getConnectionUri();

// Push the Prisma database schema
await this.pushDatabaseSchema(url);

ImportMock.mockOther(databasePackage, 'default', databasePackage.getDrizzleClient(url));

return { container};
};
}
but if you really wanted to you can just extend the return types of each of those funcs
collinbxx
collinbxx•11mo ago
I have a User creation function like this that uses the returning() function. I want to mock test it but don't know what to pass in below.
public async createUser(userData: CreateUserDto): Promise<User> {
if (isEmpty(userData)) throw new HttpException(400, "You're not userData");

const findUser: User = await DB.query.users.findFirst({
where: eq(users.email, userData.email),
});

if (findUser)
throw new HttpException(
409,
`You're email ${userData.email} already exists`,
);

const hashedPassword = await hash(userData.password, 10);
const createdUser: User = (
await DB.insert(users)
.values({
...userData,
password: hashedPassword,
})
.returning()
)[0];

return createdUser;
}
public async createUser(userData: CreateUserDto): Promise<User> {
if (isEmpty(userData)) throw new HttpException(400, "You're not userData");

const findUser: User = await DB.query.users.findFirst({
where: eq(users.email, userData.email),
});

if (findUser)
throw new HttpException(
409,
`You're email ${userData.email} already exists`,
);

const hashedPassword = await hash(userData.password, 10);
const createdUser: User = (
await DB.insert(users)
.values({
...userData,
password: hashedPassword,
})
.returning()
)[0];

return createdUser;
}
describe('[POST] /users', () => {
it('response Create user', async () => {
const userData: CreateUserDto = {
password: 'q1w2e3r4!',
};

const usersRoute = new UserRoute();

drizzleMock.query.users.findFirst.mockResolvedValueOnce(null);
drizzleMock.insert.mockReturnValueOnce(???);

const app = new App([usersRoute]);
return request(app.getServer())
.post(`${usersRoute.path}`)
.set('Authorization', `Bearer ${tokenData.token}`)
.send(userData)
.expect(201);
});
});
describe('[POST] /users', () => {
it('response Create user', async () => {
const userData: CreateUserDto = {
password: 'q1w2e3r4!',
};

const usersRoute = new UserRoute();

drizzleMock.query.users.findFirst.mockResolvedValueOnce(null);
drizzleMock.insert.mockReturnValueOnce(???);

const app = new App([usersRoute]);
return request(app.getServer())
.post(`${usersRoute.path}`)
.set('Authorization', `Bearer ${tokenData.token}`)
.send(userData)
.expect(201);
});
});
KHRM
KHRM•8mo ago
did you ever figure this out? it gives me issues when im not using the query method
No description
ashishansurkar
ashishansurkar•6mo ago
Just sharing this if any helpful to anyone: I ended up using in-memory db from @electric-sql/pglite instead of mocking. So even if I am using "drizzle-orm/node-postgres" in my actual application, tests are working as expected with "Pglite"
import { PGlite } from "@electric-sql/pglite";
import { drizzle } from "drizzle-orm/pglite";
import { migrate } from "drizzle-orm/pglite/migrator";
import path from "path";

import * as clientSchema from "../src/drizzle/schema/clients";
import * as usersSchema from "../src/drizzle/schema/users";

export const testDB = async () => {
const sqlite = new PGlite();
const db = drizzle(sqlite, { schema: { ...clientSchema, ...usersSchema } });
const migrationPath = path.join(process.cwd(), "src/drizzle/migrations");
await migrate(db, { migrationsFolder: migrationPath });
return { dblite: db };
};
import { PGlite } from "@electric-sql/pglite";
import { drizzle } from "drizzle-orm/pglite";
import { migrate } from "drizzle-orm/pglite/migrator";
import path from "path";

import * as clientSchema from "../src/drizzle/schema/clients";
import * as usersSchema from "../src/drizzle/schema/users";

export const testDB = async () => {
const sqlite = new PGlite();
const db = drizzle(sqlite, { schema: { ...clientSchema, ...usersSchema } });
const migrationPath = path.join(process.cwd(), "src/drizzle/migrations");
await migrate(db, { migrationsFolder: migrationPath });
return { dblite: db };
};
Xeras
Xeras•5mo ago
Any idea how to solve this error? It works at runtime, just vscode throws an error on this line.
KHRM
KHRM•5mo ago
i ended up just testing with another database instead of mocking it
Want results from more Discord servers?
Add your server