Nest 无法解析 AuthService 的依赖项(AuthRepository,?)

Posted

技术标签:

【中文标题】Nest 无法解析 AuthService 的依赖项(AuthRepository,?)【英文标题】:Nest can't resolve dependencies of the AuthService (AuthRepository, ?) 【发布时间】:2021-01-28 03:41:58 【问题描述】:

我正在尝试在我的项目中实施 JWT。我已经按照https://www.npmjs.com/package/@nestjs/jwt#usage中的大纲进行了操作

auth.module.ts

import  Module  from '@nestjs/common';
import  AuthService  from './auth.service';
import  AuthController  from './auth.controller';
import  TypeOrmModule  from '@nestjs/typeorm';
import  JwtModule  from '@nestjs/jwt';
import  AuthRepository  from './auth.repository';

@Module(
    imports: [
        JwtModule.register( secret: process.env.JWT_SECRET || 'ABCDE12345' ),
        TypeOrmModule.forFeature([AuthRepository]),
    ],
    exports: [TypeOrmModule, AuthService],
    providers: [AuthService],
    controllers: [AuthController],
)
export class AuthModule 

auth.service.ts

import  Injectable, NotFoundException, UnauthorizedException  from '@nestjs/common';
import  InjectRepository  from '@nestjs/typeorm';
import  AuthEntity  from './auth.entity';
import  LoginDTO  from './dto/login.dto';
import * as bcrypt from 'bcrypt';
import  JwtService  from '@nestjs/jwt';
import crypto from 'crypto';
import  AuthRepository  from './auth.repository';

// export interface FindWhereData 
//     readonly email: string;
//     readonly password: string;
// 

export interface LoginResponse 
    readonly token: string;
    readonly refresh_token: string;


@Injectable()
export class AuthService 
    constructor(
        @InjectRepository(AuthRepository)
        private authRepository: AuthRepository,
        private jwtService: JwtService
    ) 

    /**
     * Login user service
     *
     * @param doc
     */
    public async login(doc: LoginDTO): Promise<LoginResponse> 
        // verify user email
        const user = await this.authRepository.findOne( email: doc.email );

        if (!user) 
            throw new NotFoundException('Could not find user');
        
        // verify password
        const passwordsMatch = await this.passwordsAreEqual(doc.password, user.password);
        if (!passwordsMatch) 
            throw new UnauthorizedException('Incorrect login credentials');
        

        // generate JWT
        const token = await this.jwtService.signAsync( id: user.id );

        // create the refresh token
        const refreshToken = crypto.randomBytes(256).toString('hex');

        // store the refresh token

        return 
            token: token,
            refresh_token: refreshToken,
        ;
    

    /**
     * Generate a hashed password
     *
     * @param password
     */
    public async hashPassword(password: string): Promise<string> 
        const salt = await bcrypt.genSalt();

        return await bcrypt.hash(password, salt);
    

    /**
     * Compare password against an encrypted string
     *
     * @param password
     * @param encryptedPassword
     */
    public async passwordsAreEqual(password: string, encryptedPassword: string): Promise<boolean> 
        return await bcrypt.compare(password, encryptedPassword);
    

    /**
     * Find a record by column attribute and value
     *
     * @param queryObject
     *
     */
    public async findWhere(queryObject): Promise<AuthEntity> 
        const authEntity = await this.authRepository.findOne( where: queryObject );

        if (!authEntity) 
            return null;
        

        return authEntity;
    

    public async findOne(id: string): Promise<AuthEntity> 
        return this.authRepository.findOne(id);
    

auth.repository.ts

import  EntityRepository, Repository  from "typeorm";
import  AuthEntity  from "./auth.entity";

@EntityRepository(AuthEntity)
export class AuthRepository extends Repository<AuthEntity> 

app.module.ts

import  Module  from '@nestjs/common';
import  AppController  from './app.controller';
import  AppService  from './app.service';
import  RouterModule  from 'nest-router';
import  routes  from './routes';
import  ConfigModule  from '@nestjs/config';
import configuration from './config/configuration';
import  TypeOrmModule  from '@nestjs/typeorm';
import  Connection  from 'typeorm';
import  AuthModule  from './auth/auth.module';

@Module(
    imports: [
        RouterModule.forRoutes(routes),
        ConfigModule.forRoot(
            load: [configuration],
        ),
        TypeOrmModule.forRoot(
            type: 'postgres',
            host: process.env.POSTGRES_HOST || 'localhost',
            port: 5432,
            username: process.env.POSTGRES_USERNAME || 'postgres',
            password: process.env.POSTGRES_PASSWORD || 'password',
            database: process.env.POSTGRES_DATABASE || 'service-auth',
            autoLoadEntities: true,
            synchronize: true,
        ),
        AuthModule,
    ],
    controllers: [AppController],
    providers: [AppService],
)
export class AppModule 
    constructor(private readonly connection: Connection) 
        console.log('connection status: ' + connection.isConnected);
    

auth.service.spec.ts

import  JwtModule, JwtService  from '@nestjs/jwt';
import  Test, TestingModule  from '@nestjs/testing';
import  AuthEntity  from './auth.entity';
import  AuthRepository  from './auth.repository';
import  AuthService  from './auth.service';

describe('AuthService', () => 
    let authService: AuthService;
    let authRepository: AuthRepository;
    const mockAuthRepository = () => (
        login: jest.fn(),
    );

    beforeEach(async () => 
        const module: TestingModule = await Test.createTestingModule(
            providers: [
                AuthService,
                // JwtModule,
                
                    provide: getRepositoryToken(AuthRepository),
                    useFactory: mockAuthRepository,
                ,
                
                    provide: JwtService,
                    useValue: 
                        signAsync: jest.fn(() => 'token'),
                    
                
            ]
        ).compile();

        authService = await module.get<AuthService>(AuthService);
        authRepository = await module.get<AuthRepository>(AuthRepository);
    );

    /**
     * Test that the service is defined
     */
    it('should be defined', () => 
        expect(authService).toBeDefined();
    );
);

当我运行 npm run test 时,我收到以下错误消息:

FAIL  src/auth/auth.service.spec.ts
  ● AuthService › should be defined

    Nest can't resolve dependencies of the AuthService (AuthRepository, ?). Please make sure that the argument JwtService at index [1] is available in the RootTestModule context.

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

我知道经验丰富的 Node/Nest 开发人员可能很清楚该错误,但我无法弄清楚 RootTestModule 是什么以及如何导入 JwtModule。

我相信我已正确遵循设置,但将此 JwtModule 添加到 AuthService 会导致该服务在我的单元测试中未定义。

回购 https://github.com/marcuschristiansen/nestjs-auth

【问题讨论】:

【参考方案1】:

您应该为JwtService 添加一个自定义提供程序,以便您可以模拟它。自定义提供程序可能看起来像


  provide: JwtService,
  useValue: 
    signAsync: jest.fn(() => 'token'),
  

告诉 Nest 注入一个具有 signAsync() 方法的对象,该方法在调用时返回字符串 'token',以便在您的测试中始终保持相同。

这个对象进入providers 数组,就像AuthRepository 模拟

【讨论】:

请查看我对 auth.service.spec.ts 的编辑现在获取 Nest 无法解决 AuthService (?, JwtService) 的依赖关系。请确保索引 [0] 处的参数 AuthRepository 在 RootTestModule 上下文中可用。 AuthRepository 应该是 getRepositoryToken(AuthRepository) 以便您获得 Nest 期望的正确注入令牌 嗯,我觉得不错。您能否提供一个指向您的存储库的链接? 给你github.com/marcuschristiansen/nestjs-auth 您的 auth.service.spec 按预期运行。错误来自您的auth.controller.spec,因为AuthService 没有在那里模拟,所以它正在寻找AuthSerivce 的依赖项。

以上是关于Nest 无法解析 AuthService 的依赖项(AuthRepository,?)的主要内容,如果未能解决你的问题,请参考以下文章

NestJS 无法解析 AuthServices 的依赖关系

Nest 无法解析 PhotoService 的依赖关系(?)

Nest.js 在单元测试中无法解析 Mongoose 模型依赖

Nest 无法解析 UserModel 的依赖项(?)

Nest 无法解析存储库的依赖关系

Nest 无法解析 UsersService (UserModel, ?) 的依赖关系