Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nestjs unit-test - mock method guard

I have started to work with NestJS and have a question about mocking guards for unit-test. I'm trying to test a basic HTTP controller that has a method Guard attach to it.

My issue started when I injected a service to the Guard (I needed the ConfigService for the Guard).

When running the test the DI is unable to resolve the Guard

  ● AppController › root › should return "Hello World!"

    Nest can't resolve dependencies of the ForceFailGuard (?). Please make sure that the argument at index [0] is available in the _RootTestModule context.

My force fail Guard:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { ConfigService } from './config.service';

@Injectable()
export class ForceFailGuard implements CanActivate {

  constructor(
    private configService: ConfigService,
  ) {}

  canActivate(context: ExecutionContext) {
    return !this.configService.get().shouldFail;
  }
}

Spec file:

import { CanActivate } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ForceFailGuard } from './force-fail.guard';

describe('AppController', () => {
  let appController: AppController;

  beforeEach(async () => {

    const mock_ForceFailGuard = { CanActivate: jest.fn(() => true) };

    const app: TestingModule = await Test
      .createTestingModule({
        controllers: [AppController],
        providers: [
          AppService,
          ForceFailGuard,
        ],
      })
      .overrideProvider(ForceFailGuard).useValue(mock_ForceFailGuard)
      .overrideGuard(ForceFailGuard).useValue(mock_ForceFailGuard)
      .compile();

    appController = app.get<AppController>(AppController);
  });

  describe('root', () => {

    it('should return "Hello World!"', () => {
      expect(appController.getHello()).toBe('Hello World!');
    });

  });
});

I wasn't able to find examples or documentation on this issues. Am i missing something or is this a real issue ?

Appreciate any help, Thanks.

like image 365
Daniel Avatar asked Apr 25 '19 11:04

Daniel


People also ask

What is unit testing in nestjs?

Testing NestJS with unit tests The job of a unit test is to verify an individual piece of code. A tested unit can be a module, a class, or a function. Each of our tests should be isolated and independent of each other.

What is a jest unit test?

If you would like to get to know Jest better first, check out the first part of the JavaScript testing tutorial. The job of a unit test is to verify an individual piece of code. A tested unit can be a module, a class, or a function.

How do I mock a service in a unit test?

To write isolated unit tests, it’s common to mock all dependencies of a method/service. In the StudentService unit test, we’ll mock AppService by creating an ApiServiceMock class. Test Doubles: Fakes, stubs, and mocks all belong to the category of test doubles. A test double is an object or system you use in a test instead of something else.

What is nestjs?

Introduction. Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with and fully supports TypeScript (yet still enables developers to code in pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming),...


1 Answers

There are 3 issues with the example repo provided:

  1. There is a bug in Nestjs v6.1.1 with .overrideGuard() - see https://github.com/nestjs/nest/issues/2070

    I have confirmed that its fixed in 6.5.0.

  2. ForceFailGuard is in providers, but its dependency (ConfigService) is not available in the created TestingModule.

    If you want to mock ForceFailGuard, simply remove it from providers and let .overrideGuard() do its job.

  3. mock_ForceFailGuard had CanActivate as a property instead of canActivate.

Working example (nestjs v6.5.0):

import { CanActivate } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ForceFailGuard } from './force-fail.guard';

describe('AppController', () => {
  let appController: AppController;

  beforeEach(async () => {
    const mock_ForceFailGuard: CanActivate = { canActivate: jest.fn(() => true) };

    const app: TestingModule = await Test
      .createTestingModule({
        controllers: [AppController],
        providers: [
          AppService,
        ],
      })
      .overrideGuard(ForceFailGuard).useValue(mock_ForceFailGuard)
      .compile();

    appController = app.get<AppController>(AppController);
  });

  describe('root', () => {
    it('should return "Hello World!"', () => {
      expect(appController.getHello()).toBe('Hello World!');
    });
  });
});
like image 122
jdpnielsen Avatar answered Oct 03 '22 16:10

jdpnielsen