prismock

repository·master·Indexed 43 days ago

https://github.com/morintd/prismock

An 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.

Tokens
9.2K
Snippets
36
Records
43
Agent score
54%

What's inside prismock

  1. Mock a Prisma client generated to a custom path

    master

    If 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);
  2. Migrate from `generatePrismock` to `PrismockClient`

    master

    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();
  3. Mock PrismaClient automatically (Recommended)

    master

    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 };
  4. Use prismock with decimal.js

    master

    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;
  5. Understand the Delegate interface

    master

    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;
    };
  6. Use nested operations in update data

    master

    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.
  7. Troubleshoot 'createPrismock is not a function' error

    master

    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:

    1. Ensure all imports in your application and tests point to your custom client directory.
    2. Verify that the paths used when instantiating your prismock instance are correct and match your custom output path.
  8. Mock PrismaClient using Jest

    master

    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,
      };
    });
  9. Use PrismockClient manually

    master

    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);
  10. Mock `@prisma/client` using `PrismockClient` in Jest

    master

    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,
      };
    });