Skip to main content

Best Practices for Managing Test Data in NestJS with Jest

When writing unit tests for a NestJS application using Jest, effectively managing test data is crucial for ensuring reliability, maintainability, and isolation. In this guide, we'll cover how to organize, create, and utilize test data in NestJS projects following best practices.


1. Creating Test Data

Factory Functions (Dynamic Data Generation)

Factory functions allow you to create reusable mock data tailored for different test cases.

// src/collection-orders/__mocks__/collectionOrder.factory.ts
import { Types } from 'mongoose';

export const createMockCollectionOrder = (overrides = {}) => ({
  _id: new Types.ObjectId(),
  collectionVolume: 10,
  pickupRequestDate: new Date(),
  deliveryMan: new Types.ObjectId(),
  retailStore: new Types.ObjectId(),
  status: 'booking',
  ...overrides,  // Allow custom overrides
});

Fixtures (Static Data)

Fixtures are great for static, unchanging data that’s reused across multiple tests.

// src/collection-orders/__mocks__/collectionOrder.fixture.ts
export const collectionOrderFixture = {
  _id: '60b8f9f4e8d1b542f0e4c9a5',
  collectionVolume: 20,
  pickupRequestDate: '2023-01-01T00:00:00Z',
  status: 'completed',
  requestMemo: 'Handle with care',
};

๐Ÿ“ฆ 2. Organizing Test Data

Folder Structure

src/
└── collection-orders/
    ├── dto/
    ├── __mocks__/                # Test mocks & fixtures
    │   ├── collectionOrder.factory.ts
    │   └── collectionOrder.fixture.ts
    ├── collection-orders.controller.ts
    ├── collection-orders.controller.spec.ts
    ├── collection-orders.service.ts
    └── collection-orders.service.spec.ts

Purpose of Each Folder:

  • __mocks__/: Stores mock data (factories and fixtures).
  • *.spec.ts: Jest unit test files.
  • dto/: Data Transfer Objects used in the application.

๐Ÿงช 3. Using Test Data in Jest

Example: Service Unit Test

// src/collection-orders/collection-orders.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CollectionOrdersService } from './collection-orders.service';
import { getModelToken } from '@nestjs/mongoose';
import { CollectionOrder } from '@sildeswj/thechium-schema';
import { createMockCollectionOrder, collectionOrderFixture } from './__mocks__/collectionOrder.factory';

describe('CollectionOrdersService', () => {
  let service: CollectionOrdersService;
  let model: any;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CollectionOrdersService,
        {
          provide: getModelToken(CollectionOrder.name),
          useValue: {
            create: jest.fn(),
            findById: jest.fn(),
          },
        },
      ],
    }).compile();

    service = module.get<CollectionOrdersService>(CollectionOrdersService);
    model = module.get(getModelToken(CollectionOrder.name));
  });

  it('should create a collection order', async () => {
    const mockOrder = createMockCollectionOrder({ collectionVolume: 50 });
    model.create.mockResolvedValue(mockOrder);

    const result = await service.create(mockOrder);
    expect(result.collectionVolume).toBe(50);
    expect(model.create).toHaveBeenCalledWith(mockOrder);
  });

  it('should return a fixed collection order from fixture', async () => {
    model.findById.mockResolvedValue(collectionOrderFixture);

    const result = await service.findOne('60b8f9f4e8d1b542f0e4c9a5');
    expect(result.status).toBe('completed');
    expect(result.collectionVolume).toBe(20);
  });
});

๐Ÿ—„️ 4. Using In-Memory Databases (For Integration Tests)

For integration tests that require database interaction, consider using in-memory databases.

Example: MongoDB Memory Server

// src/collection-orders/__tests__/mongodb-memory.spec.ts
import { MongoMemoryServer } from 'mongodb-memory-server';
import mongoose from 'mongoose';

let mongoServer: MongoMemoryServer;

beforeAll(async () => {
  mongoServer = await MongoMemoryServer.create();
  const uri = mongoServer.getUri();
  await mongoose.connect(uri);
});

afterAll(async () => {
  await mongoose.disconnect();
  await mongoServer.stop();
});

Why Use In-Memory Databases?

  • Fast: No need for external database setups.
  • Isolated: Keeps tests independent of production data.
  • Reliable: Clean state for each test run.

๐Ÿš€ 5. Best Practices for Test Data Management

  • Isolation: Each test should create its own data to avoid dependencies.
  • Cleanup: Use afterEach to clear test data when necessary.
  • Reusability: Utilize factories for dynamic data and fixtures for static data.
  • Mock External Services: Use Jest mocks for APIs, databases, and external services.
  • Consistency: Store all test-related data within __mocks__ for easy access and maintenance.

Final Thoughts

Managing test data effectively is key to writing clean, reliable, and maintainable tests in NestJS. By using factories, fixtures, and in-memory databases, you can ensure that your tests remain isolated, consistent, and efficient.

What strategies do you use for managing test data in your NestJS projects? Share your thoughts in the comments below! ๐Ÿš€

Popular posts from this blog

Xcode and iOS Version Mismatch: Troubleshooting "Incompatible Build Number" Errors

Have you ever encountered a frustrating error while trying to run your iOS app in Xcode, leaving you scratching your head? A common issue arises when your device's iOS version is too new for the Xcode version you're using. This often manifests as an "incompatible build number" error, and looks like this: DVTDeviceOperation: Encountered a build number "" that is incompatible with DVTBuildVersion. This usually happens when you are testing with beta versions of either iOS or Xcode, and can prevent Xcode from properly compiling your storyboards. Let's explore why this occurs and what you can do to resolve it. Why This Error Occurs The core problem lies in the mismatch between the iOS version on your test device and the Software Development Kit (SDK) supported by your Xcode installation. Xcode uses the SDK to understand how to build and run apps for specific iOS versions. When your device runs a newer iOS version than Xcode anticipates, Xcode mi...

How to Fix the “Invariant Violation: TurboModuleRegistry.getEnforcing(…): ‘RNCWebView’ Could Not Be Found” Error in React Native

When working with React Native, especially when integrating additional libraries like react-native-signature-canvas , encountering errors can be frustrating. One such error is: Invariant Violation: TurboModuleRegistry. getEnforcing (...): 'RNCWebView' could not be found This error often occurs when the necessary dependencies for a module are not properly linked or when the environment you’re using doesn’t support the required native modules. Here’s a breakdown of how I encountered and resolved this issue. The Problem I was working on a React Native project where I needed to add the react-native-signature-canvas library to capture user signatures. The installation process seemed straightforward: Installed the package: npm install react-native-signature- canvas 2. Since react-native-signature-canvas depends on react-native-webview , I also installed the WebView package: npm install react- native -webview 3. I navigated to the iOS directory and ran: cd ios pod install Everythi...

Fixing FirebaseMessagingError: Requested entity was not found.

If you’re working with Firebase Cloud Messaging (FCM) and encounter the error: FirebaseMessagingError: Requested entity was not found. with the error code: messaging/registration-token-not-registered this means that the FCM registration token is invalid, expired, or unregistered . This issue can prevent push notifications from being delivered to users. ๐Ÿ” Possible Causes & Solutions 1️⃣ Invalid or Expired FCM Token FCM tokens are not permanent and may expire over time. If you’re storing tokens in your database, some might be outdated. ✅ Solution: Remove invalid tokens from your database when sending push notifications. Refresh and store the latest FCM token when the app starts. Example: Automatically Refresh Token firebase. messaging (). onTokenRefresh ( ( newToken ) => { // Send newToken to your backend and update the stored token }); 2️⃣ Token Unregistered on Client Device A token might become unregistered if: The app is uninstalled on the user’s device. ...