【问题标题】:Nest can't resolve dependencies of the AuthService (AuthRepository, ?)Nest 无法解析 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

【问题讨论】:

    标签: javascript node.js jwt nestjs jwt-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 期望的正确注入令牌
    • 嗯,我觉得不错。您能否提供一个指向您的存储库的链接?
    • 您的 auth.service.spec 按预期运行。错误来自您的auth.controller.spec,因为AuthService 没有在那里模拟,所以它正在寻找AuthSerivce 的依赖项。
    猜你喜欢
    • 2020-12-13
    • 2020-08-12
    • 2023-04-01
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-05
    • 2021-11-24
    相关资源
    最近更新 更多