【问题标题】:Nestjs unit-test - mock method guardNestjs 单元测试 - 模拟方法守卫
【发布时间】:2019-09-14 19:47:02
【问题描述】:

我已经开始使用 NestJS 并且有一个关于嘲笑守卫的问题 用于单元测试。 我正在尝试测试一个基本的 HTTP controller,它有一个 Guard 附加方法。

当我向 Guard 注入服务时,我的问题就开始了(我需要 Guard 的 ConfigService)。

运行测试时,DI 无法解析 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.

我的力量失败守卫:

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;
  }
}

规格文件:

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!');
    });

  });
});

我无法找到有关此问题的示例或文档。我错过了什么还是这是一个真正的问题?

感谢任何帮助, 谢谢。

【问题讨论】:

  • 您找到解决方案了吗?我也面临同样的问题。
  • 以后,请将相关代码粘贴到您的问题中。这使问题成为未来的证明,因为当您更改或删除您链接到的存储库时。这在这种情况下尤其需要,当这个问题是谷歌搜索“nestjs mock guard”时弹出的第一件事。

标签: unit-testing dependency-injection mocking jestjs nestjs


【解决方案1】:

提供的示例 repo 存在 3 个问题:

  1. Nestjs v6.1.1 中存在 .overrideGuard() 的错误 - 请参阅 https://github.com/nestjs/nest/issues/2070

    我已确认它已在 6.5.0 中修复。

  2. ForceFailGuardproviders 中,但它的依赖项(ConfigService)在创建的TestingModule 中不可用。

    如果您想模拟 ForceFailGuard,只需将其从 providers 中删除,然后让 .overrideGuard() 完成它的工作。

  3. mock_ForceFailGuardCanActivate 作为属性,而不是 canActivate

工作示例(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!');
    });
  });
});

【讨论】:

    【解决方案2】:

    如果除了控制器单元测试之外,您还需要/想要对您的自定义保护实现进行单元测试,您可以进行类似于下面的测试,以便预期出现错误等

    // InternalGuard.ts
    @Injectable()
    export class InternalTokenGuard implements CanActivate {
      constructor(private readonly config: ConfigService) {}
    
      public async canActivate(context: ExecutionContext): Promise<boolean> {
        const token = this.config.get('internalToken');
    
        if (!token) {
          throw new Error(`No internal token was provided.`);
        }
    
        const request = context.switchToHttp().getRequest();
        const providedToken = request.headers['authorization'];
    
        if (token !== providedToken) {
          throw new UnauthorizedException();
        }
    
        return true;
      }
    }
    

    还有你的规范文件

    beforeEach(async () => {
      const module: TestingModule = await Test.createTestingModule({
        controllers: [],
        providers: [
          InternalTokenGuard,
          {
            provide: ConfigService,
            useValue: {
              get: jest.fn((key: string) => {
                if (key === 'internalToken') {
                  return 123;
                }
                return null;
              }),
            },
          },
        ],
      }).compile();
    
      config = module.get<ConfigService>(ConfigService);
      guard = module.get<InternalTokenGuard>(InternalTokenGuard);
    });
    
    it('should throw UnauthorizedException when token is not Bearer', async () => {
      const context = {
        getClass: jest.fn(),
        getHandler: jest.fn(),
        switchToHttp: jest.fn(() => ({
          getRequest: jest.fn().mockReturnValue({
            headers: {
              authorization: 'providedToken',
            },
          }),
        })),
      } as any;
    
      await expect(guard.canActivate(context)).rejects.toThrow(
        UnauthorizedException,
      );
      expect(context.switchToHttp).toHaveBeenCalled();
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-16
      • 2020-05-29
      • 2015-10-21
      • 2020-03-22
      • 2022-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多