Note on groupBy support
mastergroupBy queries are supported, including having and orderBy. However, skip, take, and cursor are not yet supported.repository·master·Indexed 43 days ago
https://github.com/morintd/prismockAn in-memory mock for PrismaClient version 1.0.0 designed for fast, isolated, and retry-able unit testing. It reads schema.prisma to generate models and simulates Prisma's API in memory. Features include automatic mocking for Jest and Vitest via PrismockClient, manual data management using getData() and reset(), and support for custom client paths via createPrismock.
groupBy queries are supported, including having and orderBy. However, skip, take, and cursor are not yet supported.Install prismock as a development dependency after you have already set up Prisma in your project.
# Using npm
$ npm add --save-dev prismock
# Using yarn
$ yarn add -D prismockIf your Prisma client is generated to a custom directory (using the output field in your Prisma generator) instead of the default node_modules/.prisma/client, you cannot use the standard PrismockClient. Instead, you must use createPrismock and pass the Prisma object exported from your custom client path.
Note that automatic mocking via the __mock__ directory only works if the client is generated in node_modules.
import { createPrismock } from 'prismock';
import { Prisma } from './prisma/custom-client';
import { PrismaService } from './prisma.service';
const PrismockClient = createPrismock(Prisma);
const prismock = new PrismockClient();
const app = createApp(prismock);Starting from version v1.13.0, the functions generatePrismock and generatePrismockSync are deprecated and will be removed in a future version. You should replace them with the PrismockClient class. PrismockClient allows you to mock the PrismaClient directly, is easier to use because it does not rely on async functions for generation, and can be extended via class inheritance.
// Instead of generatePrismock, use PrismockClient
import { PrismockClient } from 'prismock';
const prismock = new PrismockClient();To automatically mock Prisma in your tests (compatible with Jest and Vitest), create a __mocks__/@prisma/client.ts (or .js) file at your project root. In this file, re-export everything from @prisma/client but replace PrismaClient with PrismockClient from prismock.
import { PrismockClient } from 'prismock';
export * from '@prisma/client';
export { PrismockClient as PrismaClient };By default, prismock uses structuredClone to handle data, which causes a DataCloneError when working with decimal.js objects because decimal.js types are not natively supported by the structured clone algorithm.
To resolve this, you must override the global structuredClone function with a custom implementation that serializes Decimal instances into a plain object and revives them back into Decimal instances. It is recommended to implement this workaround in a setupFilesAfterEnv file (e.g., in a Jest configuration) to ensure the global environment is patched before tests run.
// Backup the original structuredClone function
const originalStructuredClone = structuredClone;
// Custom structuredClone that handles Decimal types
function customStructuredClone(input) {
const replacer = (key, value) => {
if (value instanceof Decimal) {
// Convert Decimal to a serializable form
return {type: 'Decimal', value: value.toString()};
}
return value;
};
const reviver = (key, value) => {
if (value && value.type === 'Decimal') {
// Convert back to Decimal
return new Decimal(value.value);
}
return value;
};
// Use JSON stringify and parse as an example of handling custom types
return originalStructuredClone(JSON.parse(JSON.stringify(input, replacer), reviver));
}
// Override global structuredClone with the custom function
global.structuredClone = customStructuredClone;In prismock, a Delegate represents the mock implementation of a Prisma model's API. It provides methods that mirror the standard Prisma Client API, allowing you to perform CRUD operations on in-memory data.
Key methods include:
create(args: CreateArgs): Creates a new item.findMany(args?: FindArgs): Returns an array of items.findUnique(args: FindArgs): Returns a single item matching the criteria.update(args: UpdateArgs): Updates an existing item.delete(args: DeleteArgs): Deletes an item.count(args: FindArgs): Returns the number of items matching the criteria.upsert(args: UpsertArgs): Updates an item if it exists, otherwise creates it.onChange(items: Item[]) => void: A callback mechanism to react to data changes.Note that some methods like findUniqueOrThrow and findFirstOrThrow will reject with a PrismaClientKnownRequestError (code P2025) if no record is found, mimicking real Prisma behavior.
export type Delegate = {
create: (args: CreateArgs) => Promise<Item>;
createMany: (args: CreateManyArgs) => Promise<{ count: number }>;
delete: (args: DeleteArgs) => Promise<Item | null>;
deleteMany: (args: DeleteArgs) => Promise<{ count: number }>;
update: (args: UpdateArgs) => Promise<Item | null>;
updateMany: (args: UpdateArgs) => Promise<{ count: number }>;
upsert: (args: UpsertArgs) => Promise<Item>;
findMany: (args?: FindArgs) => Promise<Item[]>;
findUnique: (args: FindArgs) => Promise<Item>;
findFirst: (args: FindArgs) => Promise<Item>;
findUniqueOrThrow: (args: FindArgs) => Promise<Item>;
findFirstOrThrow: (args: FindArgs) => Promise<Item>;
groupBy: (args: GroupByArgs) => Promise<any[]>;
count: (args: FindArgs) => Promise<number>;
model: DMMF.Model;
getProperties: () => DelegateProperties;
getItems: () => Item[];
onChange: (items: Item[]) => void;
};The data property in UpdateArgs supports several powerful nested operations for relational fields:
connect: Connects an existing record to the current item.connectOrCreate: Attempts to connect an existing record or creates a new one if it doesn't exist.create / createMany: Creates new related records.update / updateMany: Updates existing related records.upsert: Updates a record if it exists, otherwise creates it.For list fields, you can also use:
push: Appends items to an existing list.For scalar fields, you can use atomic arithmetic operations:
increment: Adds a value to the current number.decrement: Subtracts a value from the current number.multiply: Multiplies the current number.divide: Divides the current number.set: Overwrites the field with a specific value.If you encounter the error TypeError: jest.requireActual(...).createPrismock is not a function, it usually means your code is importing @prisma/client instead of your custom client path.
To fix this:
prismock instance are correct and match your custom output path.If you prefer to mock the client directly within your test files or setupTests, use jest.mock to replace the real PrismaClient with PrismockClient.
jest.mock('@prisma/client', () => {
return {
...jest.requireActual('@prisma/client'),
PrismaClient: jest.requireActual('prismock').PrismockClient,
};
});You can manually instantiate PrismockClient and pass it to your application services or functions. This is useful for dependency injection in tests.
import { PrismockClient } from 'prismock';
import { PrismaService } from './prisma.service';
const prismock = new PrismockClient();
const app = createApp(prismock);To use prismock as a replacement for the real PrismaClient within a Jest testing environment, use jest.mock to intercept the @prisma/client module. This approach replaces the PrismaClient class with PrismockClient while preserving other actual exports from the original package.
jest.mock('@prisma/client', () => {
return {
...jest.requireActual('@prisma/client'),
PrismaClient: jest.requireActual('prismock').PrismockClient,
};
});