Модульное тестирование NestJS/TypeORM: не удается разрешить зависимости JwtService

Я пытаюсь провести модульное тестирование этого контроллера и высмеять службы / репозитории, которые ему нужны.

@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)

Я не совсем уверен, что делать дальше, поэтому мне нужна помощь.

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()и сохраните их в переменной, чтобы позже добавить к ним макеты. Вы можете проверить этот репозиторий тестирования, в котором есть много других примеров.

Если вы создаете полный набор интеграционных тестов для NestJS, то эту ошибку будет легко обнаружить, если вы импортируете module который импортирует AuthService. Это неизбежно потребует ошибки, которая выдаст ошибку: 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' }
                    })
                }),

К моему imports: [] функция внутри моего await Test.createTestingModule({ вызов

Последнее, что нужно сделать, это не импортировать напрямую. Вместо этого инициализируйте JwtModule с приведенным выше кодом, который сам по себе должен инициализировать внутреннюю JwtService правильно.

Поскольку вы регистрируетесь 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();

Другие вопросы по тегам