Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I unit test that a guard is applied on a controller in NestJS?

I've got a controller configured in NestJS and I want to check that the appropriate guards are set - does anyone have an example of how it could be done?

This (abridged) example works correctly as an application so I'm only after guidance on testing.

You'll notice in the user test there are tests where I'm calling Reflect.getMetadata. I'm after something like this - when I check it on the __guards__ metadata, this is a function and I'm struggling to mock it out so I can check that it's applied with AuthGuard('jwt') as it's setting.

User.controller.ts

@Controller('/api/user')
export class UserController {
  @UseGuards(AuthGuard('jwt'))
  @Get()
  user(@Request() req) {
    return req.user;
  }
}

User.controller.spec.ts

describe('User Controller', () => {
  // beforeEach setup as per the cli generator

  describe('#user', () => {
    beforeEach(() => {
      // This is how I'm checking the @Get() decorator is applied correctly - I'm after something for __guards__
      expect(Reflect.getMetadata('path', controller.user)).toBe('/');
      expect(Reflect.getMetadata('method', controller.user)).toBe(RequestMethod.GET);
    });

    it('should return the user', () => {
      const req = {
        user: 'userObj',
      };

      expect(controller.user(req)).toBe(req.user);
    });
  });
});
like image 858
MrSimonEmms Avatar asked Jan 16 '20 10:01

MrSimonEmms


People also ask

How does Auth guard work NestJS?

Authorization guard The AuthGuard that we'll build now assumes an authenticated user (and that, therefore, a token is attached to the request headers). It will extract and validate the token, and use the extracted information to determine whether the request can proceed or not.

Do we write unit test for controller?

We can write an unit test for this controller method by following steps: Create the test data which is returned when our service method is called. We use a concept called test data builder when we are creating the test data for our test.

How do I run a TypeScript unit test?

For TypeScript, unit tests are run against the generated JavaScript code. In most TypeScript scenarios, you can debug a unit test by setting a breakpoint in TypeScript code, right-clicking a test in Test Explorer, and choosing Debug.

What is unit testing in Java?

Unit testing focuses on writing tests for the smallest possible units. In most cases, they are functions defined in classes. MethodA in a class may be calling MethodB in another class. However, a unit test of MethodA is focused only on the logic of MethodA, not MethodB.

What is jest test runner in nestjs?

We will use Jest as Runner for Running tests for NestJS Lets first talk about Jest a little bit which is a popular test runner Jest is a JavaScript test runner, that is, a JavaScript library for creating, running, and structuring tests. Jest ships as an NPM package, you can install it in any JavaScript project.

How do I test a guard class?

If you are just looking to test the guard, you can instantiate the GuardClass directly and test its canActivate method by providing an ExecutionContext object. I've got an example here. The example uses a library that creates mock objects for you (since then renamed ), but the idea of it is that you'd create an object like

How to test if a guard is executed when calling a route?

If you're wanting to test that when you call the route the guard is executed, then you need to set up supertest to make the call to the route. Maybe I'm not understanding what you're looking to do


2 Answers

For what it's worth, you shouldn't need to test that the decorators provided by the framework set what you expect them too. That's why the framework has tests on them to begin with. Nevertheless, if you want to check that the decorator actually sets the expected metadata you can see that done here.

If you are just looking to test the guard, you can instantiate the GuardClass directly and test its canActivate method by providing an ExecutionContext object. I've got an example here. The example uses a library that creates mock objects for you (since then renamed), but the idea of it is that you'd create an object like

const mockExecutionContext: Partial<
  Record<
    jest.FunctionPropertyNames<ExecutionContext>,
    jest.MockedFunction<any>
  >
> = {
  switchToHttp: jest.fn().mockReturnValue({
    getRequest: jest.fn(),
    getResponse: jest.fn(),
  }),
};

Where getRequest and getResponse return HTTP Request and Response objects (or at least partials of them). To just use this object, you'll need to also use as any to keep Typescript from complaining too much.

like image 118
Jay McDoniel Avatar answered Sep 18 '22 04:09

Jay McDoniel


I realize its not quite the answer you are looking for, but building on @Jay McDoniel's answer I used the following to test a custom decorator's existence on a controller function (although i'm not 100% sure if this is the correct way to test this for non-custom guards)

import { Controller } from '@nestjs/common';
import { UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './jwtAuthGuard';

@Controller()
export class MyController {

  @UseGuards(JwtAuthGuard)
  user() {
    ...
  }
}
it('should ensure the JwtAuthGuard is applied to the user method', async () => {
  const guards = Reflect.getMetadata('__guards__', MyController.prototype.user)
  const guard = new (guards[0])

  expect(guard).toBeInstanceOf(JwtAuthGuard)
});

And for controllers

it('should ensure the JwtAuthGuard is applied to the controller', async () => {
  const guards = Reflect.getMetadata('__guards__', MyController)
  const guard = new (guards[0])

  expect(guard).toBeInstanceOf(JwtAuthGuard)
});
like image 40
myol Avatar answered Sep 19 '22 04:09

myol