Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Nestjs interceptor?

I can't find any explanation on how to test interceptors in NestJS

This simple example intercepts a POST query to add an attribute to an Example Model provided in the body.

@Injectable()
export class SubscriberInterceptor implements NestInterceptor {
  async intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Promise<Observable<ExampleModel>> {
    let body: ExampleModel = context.switchToHttp().getRequest().body;
    body = {
      ...body,
      addedAttribute: 'example',
    };
    context.switchToHttp().getRequest().body = body;
    return next.handle();
  }
}

I would like to test what's happening in the intercept function.

So far:

const interceptor = new SubscriberInterceptor();

describe('SubscriberInterceptor', () => {
  it('should be defined', () => {
    expect(interceptor).toBeDefined();
  });

  describe('#intercept', () => {
    it('should add the addedAttribute to the body', async () => {
      expect(await interceptor.intercept(arg1, arg2)).toBe({ ...bodyMock, addedAttribute: 'example' });
    });
  });
});

My question: Should I mock only arg1: ExecutionContext and arg2: CallHandler? If so, how to mock arg1 and arg2? Else How should I proceed?

like image 852
Matt Walterspieler Avatar asked Aug 30 '19 16:08

Matt Walterspieler


People also ask

How do I test my nest interceptor?

NestJS Interceptor under the hood uses RxJS. So, when its intercept() method is called by the framework, it returns an Observable of our object. To cleanly extract our value from this Observable , we use the convenience function lastValueFrom() from RxJS. The testUser here, is your object under test.

How do you make an interceptor in NestJS?

In order to set up the interceptor, we use the @UseInterceptors() decorator imported from the @nestjs/common package. Like pipes and guards, interceptors can be controller-scoped, method-scoped, or global-scoped. Hint The @UseInterceptors() decorator is imported from the @nestjs/common package.


1 Answers

You are right, you should mock the arg1 and arg2,then pass them to intercept method, here is the solution:

SubscriberInterceptor.ts:

interface ExecutionContext {
  switchToHttp(): any;
}
interface CallHandler {
  handle(): any;
}
interface Observable<T> {}
interface ExampleModel {}

interface NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<ExampleModel>>;
}

export class SubscriberInterceptor implements NestInterceptor {
  public async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<ExampleModel>> {
    let body: ExampleModel = context.switchToHttp().getRequest().body;
    body = {
      ...body,
      addedAttribute: 'example'
    };
    context.switchToHttp().getRequest().body = body;
    return next.handle();
  }
}

Unit test, mock chained method of executionContext

import { SubscriberInterceptor } from './';

const interceptor = new SubscriberInterceptor();

const executionContext = {
  switchToHttp: jest.fn().mockReturnThis(),
  getRequest: jest.fn().mockReturnThis()
};

const callHandler = {
  handle: jest.fn()
};

describe('SubscriberInterceptor', () => {
  it('should be defined', () => {
    expect(interceptor).toBeDefined();
  });
  describe('#intercept', () => {
    it('t1', async () => {
      (executionContext.switchToHttp().getRequest as jest.Mock<any, any>).mockReturnValueOnce({
        body: { data: 'mocked data' }
      });
      callHandler.handle.mockResolvedValueOnce('next handle');
      const actualValue = await interceptor.intercept(executionContext, callHandler);
      expect(actualValue).toBe('next handle');
      expect(executionContext.switchToHttp().getRequest().body).toEqual({
        data: 'mocked data',
        addedAttribute: 'example'
      });
      expect(callHandler.handle).toBeCalledTimes(1);
    });
  });
});

Unit test result:

 PASS  src/mock-function/57730120/index.spec.ts
  SubscriberInterceptor
    ✓ should be defined (10ms)
    #intercept
      ✓ t1 (11ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.235s, estimated 3s
like image 174
slideshowp2 Avatar answered Oct 06 '22 01:10

slideshowp2