【问题标题】:NestJS/TypeORM unit testing: Can't resolve dependencies of JwtServiceNestJS/TypeORM 单元测试:无法解析 JwtService 的依赖关系
【发布时间】:2020-10-30 12:36:57
【问题描述】:

我正在尝试对这个控制器进行单元测试并模拟它需要的服务/存储库。

@Controller('auth')
export class AuthController {
    constructor(
        private readonly authService: AuthService,
        private readonly usersService: UsersService,
    ) {}

    @Post('register')
    public async registerAsync(@Body() createUserModel: CreateUserModel) {
        const result = await this.authenticationService.registerUserAsync(createUserModel);

        // more code here
    }

    @Post('login')
    public async loginAsync(@Body() login: LoginModel): Promise<{ accessToken: string }> {
        const user = await this.usersService.getUserByUsernameAsync(login.username);

        // more code here
    }
}

这是我的单元测试文件:

describe('AuthController', () => {
    let authController: AuthController;
    let authService: AuthService;

    beforeEach(async () => {
        const moduleRef: TestingModule = await Test.createTestingModule({
            imports: [JwtModule],
            controllers: [AuthController],
            providers: [
                AuthService,
                UsersService,
                {
                    provide: getRepositoryToken(User),
                    useClass: Repository,
                },
            ],
        }).compile();

        authController = moduleRef.get<AuthenticationController>(AuthenticationController);
        authService = moduleRef.get<AuthenticationService>(AuthenticationService);
    });

    describe('registerAsync', () => {
        it('Returns registration status when user registration succeeds', async () => {
            let createUserModel: CreateUserModel = {...}

            let registrationStatus: RegistrationStatus = {
                success: true,
                message: 'User registered successfully',
            };

            jest.spyOn(authService, 'registerUserAsync').mockImplementation(() =>
                Promise.resolve(registrationStatus),
            );

            expect(await authController.registerAsync(createUserModel)).toEqual(registrationStatus);
        });
    });
});

但是在运行时,我收到以下错误:

  ● AuthController › registerAsync › Returns registration status when user registration succeeds

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

    Potential solutions:
    - If JWT_MODULE_OPTIONS is a provider, is it part of the current JwtModule?
    - If JWT_MODULE_OPTIONS is exported from a separate @Module, is that module imported within JwtModule?
      @Module({
        imports: [ /* the Module containing JWT_MODULE_OPTIONS */ ]
      })

      at Injector.lookupComponentInParentModules (../node_modules/@nestjs/core/injector/injector.js:191:19)
      at Injector.resolveComponentInstance (../node_modules/@nestjs/core/injector/injector.js:147:33)
      at resolveParam (../node_modules/@nestjs/core/injector/injector.js:101:38)
          at async Promise.all (index 0)
      at Injector.resolveConstructorParams (../node_modules/@nestjs/core/injector/injector.js:116:27)
      at Injector.loadInstance (../node_modules/@nestjs/core/injector/injector.js:80:9)
      at Injector.loadProvider (../node_modules/@nestjs/core/injector/injector.js:37:9)
      at Injector.lookupComponentInImports (../node_modules/@nestjs/core/injector/injector.js:223:17)
      at Injector.lookupComponentInParentModules (../node_modules/@nestjs/core/injector/injector.js:189:33)

  ● AuthController › registerAsync › Returns registration status when user registration succeeds

    Cannot spyOn on a primitive value; undefined given

      48 |             };
      49 |
    > 50 |             jest.spyOn(authService, 'registerUserAsync').mockImplementation(() =>
         |                  ^
      51 |                 Promise.resolve(registrationStatus),
      52 |             );
      53 |

      at ModuleMockerClass.spyOn (../node_modules/jest-mock/build/index.js:780:13)
      at Object.<anonymous> (Authentication/authentication.controller.spec.ts:50:18)

我不太确定如何继续,所以我需要一些帮助。

【问题讨论】:

    标签: jestjs nestjs typeorm nestjs-jwt


    【解决方案1】:

    如果您正在为 NestJS 构建一个完整的集成测试套件,那么如果您导入一个导入 AuthServicemodule,则很容易遇到此错误。这将不可避免地需要JwtService,它会出错:Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0] is available in the RootTestModule context.

    这是我解决此问题的方法。我补充说:

                    JwtModule.registerAsync({
                        imports: [ConfigModule],
                        inject: [ConfigService],
                        useFactory: async (configService: ConfigService) => ({
                            secret: configService.get('JWT_SECRET'),
                            signOptions: { expiresIn: '1d' }
                        })
                    }),
    

    到我的await Test.createTestingModule({ 调用中的imports: [] 函数

    最后要做的重要事情是直接导入JwtService。相反,使用上面的代码初始化JwtModule,通过扩展本身应该在内部正确初始化JwtService

    【讨论】:

      【解决方案2】:

      由于您在依赖注入容器中注册AuthService 并且只是监视registerUserAsync,因此还需要注册JWTService

      需要注册AuthService中注入的依赖:

      const moduleRef: TestingModule = await Test.createTestingModule({
        imports: [JwtModule],
        controllers: [AuthController],
        providers: [
          AuthService,
          UsersService,
          JWTService, // <--here
          {
            provide: getRepositoryToken(User),
            useClass: Repository,
          },
      ],
      }).compile();
      
      

      或注册一个完全模拟的AuthService,不需要任何其他依赖:

      const moduleRef: TestingModule = await Test.createTestingModule({
        imports: [JwtModule],
        controllers: [AuthController],
        providers: [
          {
            provide: AuthService,
            useValue: {
              registerUserAsync: jest.fn(), // <--here
            }
           },
          {
            provide: getRepositoryToken(User),
            useClass: Repository,
          },
      ],
      }).compile();
      
      

      【讨论】:

        【解决方案3】:

        我在这里注意到了一些事情:

        1. 如果您正在测试控制器,则不需要模拟超过一层的深度 pf 服务

        2. 在单元测试中,您几乎不应该遇到需要 imports 数组的用例。

        您可以为您的测试用例做的事情类似于如下:

        beforeEach(async () => {
          const modRef = await Test.createTestingModule({
            controllers: [AuthController],
            providers: [
              {
                provide: AuthService,
                useValue: {
                  registerUserAsync: jest.fn(),
                }
        
              },
              {
                provide: UserService,
                useValue: {
                  getUserByUsernameAsync: jest.fn(),
                }
              }
            ]
          }).compile();
        });
        

        现在您可以使用modRef.get() 获取身份验证服务和用户服务,并将它们保存到变量中,以便稍后向它们添加模拟。您可以查看this testing repository,其中有很多其他示例。

        【讨论】:

          猜你喜欢
          • 2021-11-17
          • 2021-09-06
          • 2019-12-19
          • 2018-11-22
          • 2019-12-19
          • 2018-12-25
          • 2019-02-24
          • 2020-08-12
          • 2021-11-24
          相关资源
          最近更新 更多