How to Mock a Module Jest-Style in Bun: Testing NodeJS Projects with Bun’s Built-in Framework in 2024

Excerpt: Learn how to mock a module Jest-style while testing your NodeJS projects using Bun’s built-in testing framework. This tutorial will guide you through setting up and testing a CustomerService with Prisma ORM, demonstrating how to mock the Prisma client for efficient testing without real database interactions.

1. Introduction

In this tutorial, we’ll explore how to test a NodeJS project using Bun’s built-in testing framework instead of the traditional Jest approach. We’ll focus on a CustomerService that manages customer data, using Prisma as an ORM. The key aspect we’ll cover is how to mock the Prisma client to avoid real database interactions during testing.

2. Project Setup

First, clone the project repository:

Bash
git clone https://github.com/NickyBall/bun-service-prisma-mock-tutorial
cd bun-service-prisma-mock-tutorial

Install dependencies:

Bash
bun install

3. Project Structure

Our project consists of:

  1. CustomerService: Manages customer data
  2. Customer model: Contains id and name fields
  3. Prisma ORM for database interactions

Let’s take a look at the key files:

3.1 schema.prisma

Prisma
datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

generator client {
  provider = "prisma-client-js"
}

model Customer {
  id    Int    @id @default(autoincrement())
  name  String
}

This schema defines our Customer model with an auto-incrementing id and a name field.

3.2 CustomerService.ts

TypeScript
import type { PrismaClient } from "@prisma/client";

class CustomerService {
  private prisma: PrismaClient;

  constructor(prisma: PrismaClient) {
    this.prisma = prisma;
  }

  async getAllCustomers() {
    return this.prisma.customer.findMany();
  }

  async getCustomerById(id: number) {
    return this.prisma.customer.findUnique({
      where: { id },
    });
  }

  async createCustomer(name: string) {
    return this.prisma.customer.create({
      data: { name },
    });
  }

  async updateCustomer(id: number, name: string) {
    return this.prisma.customer.update({
      where: { id },
      data: { name },
    });
  }

  async deleteCustomer(id: number) {
    return this.prisma.customer.delete({
      where: { id },
    });
  }
}

export default CustomerService;

This CustomerService class provides methods for CRUD operations on customer data, using Prisma ORM to interact with the database.

4. Writing Tests with Bun

Bun comes with a built-in testing framework that’s similar to Jest. Here’s how we write our tests in CustomerService.test.ts:

TypeScript
import { describe, it, expect, beforeEach, mock } from "bun:test";
import { PrismaClient } from "@prisma/client";
import CustomerService from "../services/CustomerService";

// Mock Customers
const mockCustomers = [
  { id: 1, name: "John Doe" },
  { id: 2, name: "Jane Doe" },
];

// Mock PrismaClient
const prismaMock = {
  customer: {
    findMany: mock(() => mockCustomers),
    findUnique: mock((query) =>
      mockCustomers.find((c) => c.id === query.where.id)
    ),
    create: mock((query) => {
      const customer = {
        id: mockCustomers.length + 1,
        name: query.data.name,
      };
      mockCustomers.push(customer);
      return customer;
    }),
    update: mock((query) => {
      const customer = mockCustomers.find((c) => c.id === query.where.id);
      if (customer) {
        customer.name = query.data.name;
      }
    }),
    delete: mock((query) => {
      const index = mockCustomers.findIndex((c) => c.id === query.where.id);
      if (index !== -1) {
        mockCustomers.splice(index, 1);
      }
    }),
  },
};

mock.module("@prisma/client", () => {
  return { PrismaClient: mock(() => prismaMock) };
});

const prisma = new PrismaClient();
const customerService = new CustomerService(prisma);

describe("CustomerService", () => {
  beforeEach(() => {
    prismaMock.customer.findMany.mockClear();
    prismaMock.customer.findUnique.mockClear();
    prismaMock.customer.create.mockClear();
    prismaMock.customer.update.mockClear();
    prismaMock.customer.delete.mockClear();
  });

  it("should fetch all customers", async () => {
    const customers = await customerService.getAllCustomers();
    expect(customers).toEqual(mockCustomers);
    expect(prismaMock.customer.findMany).toHaveBeenCalledTimes(1);
  });

  it("should fetch a customer by ID", async () => {
    const customer = await customerService.getCustomerById(1);
    expect(customer).toEqual(mockCustomers[0]);
    expect(prismaMock.customer.findUnique).toHaveBeenCalledWith({
      where: { id: 1 },
    });
  });

  it("should create a customer", async () => {
    const customer = await customerService.createCustomer("Alice Doe");
    expect(customer.id).toEqual(3);
    expect(prismaMock.customer.create).toHaveBeenCalledWith({
      data: { name: "Alice Doe" },
    });
  });

  it("should update a customer", async () => {
    await customerService.updateCustomer(1, "James Doe");
    expect(mockCustomers[0].name).toEqual("James Doe");
    expect(prismaMock.customer.update).toHaveBeenCalledWith({
      where: { id: 1 },
      data: { name: "James Doe" },
    });
  });

  it("should delete a customer", async () => {
    await customerService.deleteCustomer(1);
    expect(mockCustomers.find((t) => t.id === 1)).not.toBeDefined();
    expect(prismaMock.customer.delete).toHaveBeenCalledWith({
      where: { id: 1 },
    });
  });
});

5. Mock a module jest-like for the Prisma Client

To mock the Prisma client, we create a mock object that mimics the structure and methods of the real PrismaClient. We use Bun’s mock function to create mock implementations of the methods we want to test.

The key part of our mocking strategy is:

TypeScript
mock.module("@prisma/client", () => {
  return { PrismaClient: mock(() => prismaMock) };
});

This mocks the entire @prisma/client module, replacing the PrismaClient with our mock implementation. This allows us to test our CustomerService without actually interacting with a database.

6. Running Tests

To run the tests, use the following command:

Bash
bun test

Bun will automatically detect and run all test files in your project.

bun test result

7. Conclusion

In this tutorial, we’ve learned how to mock a module Jest-style while using Bun’s built-in testing framework. We’ve set up tests for a CustomerService, demonstrating how to mock the Prisma client for efficient testing without real database interactions.

Key takeaways:

  1. We use Bun’s mock function to create mock implementations of Prisma methods.
  2. We mock the entire @prisma/client module to replace the real PrismaClient with our mock.
  3. We write tests for each method of our CustomerService, ensuring they interact correctly with the mocked Prisma client.

By using Bun’s testing capabilities, we can streamline our development process and take advantage of Bun’s speed and efficiency. This approach allows for fast, reliable tests that don’t depend on external services, making our test suite more robust and easier to maintain.

Leave a Comment