简体   繁体   中英

Mock nestjs decorator

I am using a custom Firewall decorator that provides some utility functionality for many of my endpoints (eg throttling, authorization etc.) and I want be able to mock this decorator in my endpoints:

@Controller('some')
export class SomeController {
  @Firewall() // mock it and check that it's called with correct arguments
  async testEndpoint() {
    return 'test';
  }
}

I want to mock it and check that it's called with the correct parameters, but I can't figure out how I can do this in my test cases:

import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { AppModule } from 'src/app.module';

describe('Some Controller', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleRef.createNestApplication();
    await app.init();
  });

  it('some testcase', () => {
    // What do I do here to mock my Firewall decorator? // <--- <--- <---
    return request(app.getHttpServer()).get('/some/testEndpoint').expect(401);
  });

  afterAll(async () => {
    await app.close();
  });
});

If it can help, here is a short version of the Firewall decorator:

import { applyDecorators } from '@nestjs/common';
import { Throttle, SkipThrottle } from '@nestjs/throttler';

export function Firewall(options?: { skipThrottle?: boolean }) {
  const { skipThrottle } = options || {
    anonymous: false,
  };

  const decorators = [];
  if (skipThrottle) {
    decorators.push(SkipThrottle());
  } else {
    decorators.push(Throttle(10, 10));
  }

  return applyDecorators(...decorators);
}

I have checked other answers (including this one ) but they didn't help.

Thanks in advance for your time!

The @Throttle() and @SkipThrottle() decorators only apply metadata to the controller / controller method they decorate. They don't do anything on their own. Your custom @Firewall() is a utility decorator to combine these into a single decorator for convenience.

If you take a look at the source code of the nestjs/throttler package you'll see it is the @ThrottlerGuard() guard that retrieves this metadata and actually does the throttling.

I suspect you configured this one as a global app guard, so it is applied for all requests.

@Module({
  imports: [
    ThrottlerModule.forRoot({...}),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}

In your test you need to mock the ThrottlerGuard .

const ThrottlerGuardMock = {
  canActivate(ctx) {
    const request = ctx.switchToHttp().getRequest();
    // mock implementation goes here
    // ...
    return true;
  }
} as ThrottlerGuard;

const module = await Test.createTestModule({
  imports: [AppModule]
})
  .overrideProvider(ThrottlerGuard)
  .useValue(ThrottlerGuardMock) // <-- magic happens here
  .compile();
app = moduleRef.createNestApplication();
await app.init();

You could setup some spies, in the mocked guard retrieve the metadata set by the decorators applied by the @Firewall() decorator and then invoke the spies with that metadata. Then you could just verify if the spies were called with the expected values. That would verify that your custom decorator passed down the expected values to the throttle decorators. Actually testing the @ThrottleGuard() decorator is the nestjs/throttler package's responsibility.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM