PrismaMiddleTechnical

Как тестировать код, использующий Prisma? Какие стратегии мокирования доступны?

Для unit-тестов используйте jest-mock-extended для мокирования Prisma Client; для интеграционных тестов — отдельную тестовую БД с prisma migrate deploy в beforeAll.

Стратегии тестирования кода с Prisma

Для тестирования кода, использующего Prisma, применяют три основных подхода: мокирование Prisma Client, использование тестовой базы данных с миграциями и использование in-memory/SQLite.

1. Мокирование с jest-mock-extended

Наиболее популярный подход для unit-тестов — создать мок всего Prisma Client:

// src/__mocks__/prisma.ts
import { PrismaClient } from '@prisma/client';
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended';

export const prismaMock = mockDeep<PrismaClient>();

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

// jest.setup.ts
jest.mock('../prisma', () => ({
  __esModule: true,
  prisma: prismaMock,
}));

В тесте используем типизированный мок:

// user.service.test.ts
import { prismaMock } from '../__mocks__/prisma';
import { createUser } from './user.service';

test('createUser сохраняет пользователя', async () => {
  const user = { id: 1, email: 'test@example.com', name: 'Alice' };
  prismaMock.user.create.mockResolvedValue(user);

  const result = await createUser({ email: 'test@example.com', name: 'Alice' });
  expect(result).toEqual(user);
  expect(prismaMock.user.create).toHaveBeenCalledWith({
    data: { email: 'test@example.com', name: 'Alice' },
  });
});

2. Тестовая база данных (интеграционные тесты)

Для интеграционных тестов используют отдельную БД (PostgreSQL или SQLite) и прогоняют миграции перед тестами:

// jest.integration.setup.ts
import { execSync } from 'child_process';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

beforeAll(async () => {
  // DATABASE_URL должен указывать на тестовую БД
  execSync('npx prisma migrate deploy', {
    env: { ...process.env, DATABASE_URL: process.env.TEST_DATABASE_URL },
  });
});

afterEach(async () => {
  // Очищаем таблицы между тестами
  await prisma.user.deleteMany();
  await prisma.post.deleteMany();
});

afterAll(async () => {
  await prisma.$disconnect();
});

3. SQLite для быстрых интеграционных тестов

Если схема совместима, можно использовать SQLite вместо PostgreSQL — это быстрее в CI:

# .env.test
DATABASE_URL="file:./test.db"
DATABASE_URL="file:./test.db" npx prisma migrate deploy
DATABASE_URL="file:./test.db" npx jest

4. Singleton-паттерн для Prisma Client

Чтобы избежать множества открытых соединений в тестах, используют singleton:

// src/prisma.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = global as unknown as { prisma: PrismaClient };

export const prisma =
  globalForPrisma.prisma ||
  new PrismaClient({ log: ['error'] });

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma;
}

Подводные камни

  • Не забывайте вызывать mockReset в beforeEach — иначе моки из предыдущего теста просочатся в следующий.
  • Мок Prisma Client не проверяет реальные SQL-ограничения (UNIQUE, FK) — интеграционные тесты обязательны для критических путей.
  • При использовании SQLite учитывайте несовместимость с PostgreSQL-специфичными типами (jsonb, uuid, enum).
  • Миграции в CI должны применяться к чистой тестовой БД, иначе флакообразные ошибки «relation already exists».
  • После изменения схемы обязательно перегенерировать Prisma Client (npx prisma generate), иначе типы в моках устареют.
  • Не вызывайте prisma.$disconnect() между тестами — только в afterAll, иначе connection pool исчерпается.
  • Очистка данных через deleteMany медленнее TRUNCATE ... CASCADE; в больших схемах используйте raw-запрос.
  • В jest-mock-extended мок транзакций (prisma.$transaction) требует отдельной настройки — не будет работать по умолчанию.

Common mistakes

  • Путает Prisma Client API с гарантиями базы данных: индексы, блокировки и isolation level не создаются магически.
  • Не объясняет, где в lifecycle находится тестирование кода с Prisma.
  • Не разделяет validation, authorization, business logic и persistence.
  • Игнорирует ошибки, лимиты входных данных, observability и тестирование.

What the interviewer is testing

  • Может объяснить тестирование кода с Prisma на примере кода.
  • Называет ключевые API: test database, mock PrismaClient.
  • Отделяет ORM/query builder поведение от реального поведения СУБД.
  • Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.

Sources

Related topics