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

时间:2020-10-13 18:47:12

标签: javascript node.js jwt nestjs jwt-auth

我正在尝试将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 个答案:

答案 0 :(得分:0)

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

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

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

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