<link rel="stylesheet" href="styles-3GBANQL6.css">
← Back to blog

NestJS

Advanced Testing Strategies with Mocks in NestJS

Advanced Testing Strategies with Mocks in NestJS

Introduction

When testing applications in NestJS, mocks are a powerful tool to decouple your test environment from external dependencies. This makes it easier to test the logic without worrying about complex integrations or database connections.

In this post, we'll discuss advanced testing strategies for NestJS modules and services using mocks, along with practical examples.

Benefits of Mocking

During testing, it's often necessary to mock dependencies like databases, APIs, or external services. Using mocks provides several benefits:

  • Isolation: Tests can be executed independently of real external services.
  • Performance: Since no real databases or APIs are used, the tests run faster.
  • Predictability: Mocks provide fixed responses, improving the reproducibility of tests.

Creating Mocks in NestJS

In NestJS, mocks can be created in various ways, but the most common approach is using jest.mock or manually providing mock values. Here’s a simple example:


// foo.service.ts
@Injectable()
export class FooService {
  constructor(private readonly barService: BarService) {}

  async getFoo(): Promise {
    const bar = await this.barService.getBar();
    return `Foo says: ${bar}`;
  }
}
    

Let’s say we want to mock BarService in a test case without calling the actual service.


// foo.service.spec.ts
describe('FooService', () => {
  let fooService: FooService;
  let barService: BarService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        FooService,
        {
          provide: BarService,
          useValue: { getBar: jest.fn().mockResolvedValue('mocked Bar') },
        },
      ],
    }).compile();

    fooService = module.get(FooService);
    barService = module.get(BarService);
  });

  it('should return mocked Bar', async () => {
    expect(await fooService.getFoo()).toBe('Foo says: mocked Bar');
  });
});
    

In this example, we replace BarService with a mock that returns a fixed value. This allows us to test the logic of FooService in isolation.

Using jest.spyOn to Verify Method Calls

jest.spyOn allows us to verify whether certain methods in our services were called. This is particularly useful when we need to ensure that dependencies are used correctly.


describe('FooService with jest.spyOn', () => {
  let fooService: FooService;
  let barService: BarService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [FooService, BarService],
    }).compile();

    fooService = module.get(FooService);
    barService = module.get(BarService);
  });

  it('should call BarService.getBar', async () => {
    const spy = jest.spyOn(barService, 'getBar').mockResolvedValue('spied Bar');
    await fooService.getFoo();
    expect(spy).toHaveBeenCalled();
    expect(spy).toHaveBeenCalledWith(); // verifies that no arguments were passed
  });
});
    

In this test, we check that the getBar() method in BarService was indeed called. Additionally, the spy returns a mocked response.

Mocking Asynchronous Dependencies

It’s common for services in NestJS to be asynchronous. Even in these cases, mocks can be used. Here’s an example:


// bar.service.ts
@Injectable()
export class BarService {
  async getBar(): Promise {
    return 'Bar';
  }
}
    

// foo.service.spec.ts - Mock for async method
it('should return mocked async Bar', async () => {
  jest.spyOn(barService, 'getBar').mockResolvedValue('async mocked Bar');
  const result = await fooService.getFoo();
  expect(result).toBe('Foo says: async mocked Bar');
});
    

In this test, we simulate an asynchronous response from BarService.getBar() by using mockResolvedValue().

Using Test.createTestingModule() for Mock Creation

The NestJS testing module allows us to create test environments and mock dependencies easily. This is especially useful when we have multiple dependencies in a module but only want to mock certain ones.


const module: TestingModule = await Test.createTestingModule({
  providers: [
    FooService,
    {
      provide: BarService,
      useValue: { getBar: jest.fn().mockResolvedValue('mocked Bar') },
    },
    AnotherService, // other real dependency
  ],
}).compile();
    

Here, we mock only BarService while other dependencies like AnotherService remain real implementations.

Conclusion

Advanced testing strategies with mocks in NestJS allow us to test services in isolation and with efficiency. By using mocks, we ensure that our code functions correctly without relying on external dependencies or slow database connections.

The examples we’ve covered demonstrate how to use mocks with NestJS and Jest to isolate services, test asynchronous dependencies, and monitor method calls. These techniques are especially useful in larger projects where clear separation of logic from its dependencies is essential.

LET'S BUILD WHAT'S NEXT

Ready to turn your idea into a real solution?

Let's discuss how we can help you create value with technology.

Let's Talk