Estoy tratando de simular la implementación del método hash bcrypt, pero obtengo el siguiente error:
Error: thrown: "Exceeded timeout of 5000 ms for a test. Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."He intentado aumentar el tiempo de espera hasta 30000. También he intentado simular todo el módulo bcrypt como jest.mock('bcrypt'). Soy nuevo en las pruebas y puede haber algunos errores lógicos o malas prácticas. Le agradeceré que los señale.
Mi código:
import { UserService } from '../user.service'; import { Test, TestingModule } from '@nestjs/testing'; import { Repository } from 'typeorm'; import { getRepositoryToken } from '@nestjs/typeorm'; import * as bcrypt from 'bcrypt'; import { UserEntity } from '../user.entity'; import { UserRepository } from '../user.repository'; import { CreateUserDto } from '../dto/create-user.dto'; import { userStub } from './stubs/user.stub'; describe('UserService', () => { let userService: UserService; let userRepository: Repository<UserEntity>; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UserService, { provide: getRepositoryToken(UserRepository), useClass: Repository, }, ], }).compile(); userService = module.get<UserService>(UserService); userRepository = module.get<Repository<UserEntity>>( getRepositoryToken(UserRepository), ); }); it('should define UserService', () => { expect(userService).toBeDefined(); }); it('should define userRepository', () => { expect(userRepository).toBeDefined(); }); describe('createUser method', () => { it('has called with valid data', async () => { const createUserDto: CreateUserDto = { email: userStub().email, firstName: userStub().firstName, lastName: userStub().lastName, password: userStub().password, }; const user: UserEntity = userStub(); const spiedBcryptHashMethod = jest .spyOn(bcrypt, 'hash') .mockImplementation(() => Promise.resolve('')); const spiedRepositoryCreateMethod = jest .spyOn(userRepository, 'create') .mockReturnValue(user); const spiedRepositorySaveMethod = jest .spyOn(userRepository, 'save') .mockResolvedValue(user); const createUserResult = await userService.createUser(createUserDto); expect(spiedBcryptHashMethod).toHaveBeenCalled(); expect(spiedRepositoryCreateMethod).toHaveBeenCalled(); expect(spiedRepositorySaveMethod).toHaveBeenCalledWith(user); expect(createUserResult).toEqual(user); }); }); });El error aparece aquí:
const spiedBcryptHashMethod = jest .spyOn(bcrypt, 'hash') .mockImplementation(() => Promise.resolve(''));Mi código de servicio de usuario:
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import * as bcrypt from 'bcrypt'; import { UserRepository } from './user.repository'; import { CreateUserDto } from './dto/create-user.dto'; import { UserEntity } from './user.entity'; @Injectable() export class UserService { constructor( @InjectRepository(UserRepository) private userRepository: UserRepository, ) {} public async createUser(createUserDto: CreateUserDto): Promise<UserEntity> { return await this.userRepository.save( this.userRepository.create({ ...createUserDto, password: await new Promise((resolve, reject) => { bcrypt.hash(createUserDto.password, 10, (err, encrypted) => { if (err) { reject(err); } resolve(encrypted); }); }).then((onFilled: string) => onFilled), }), ); } }Bien, entonces hay mucho que decir sobre su código de servicio...
El problema inmediato que tiene es que se está burlando del método hash de bcrypt para devolver una promesa, pero usa el método cuando devuelve una devolución de llamada. SI desea seguir usando la devolución de llamada combinada con el enfoque de promesas, debe hacer algo como
jest.spyOn(bcrypt, 'hash').mockImplementation((pass, salt, cb) => cb(null, '')) Esto será esencialmente lo mismo que Promise.resolve('') .
SIN EMBARGO , no sugiero esto. Bcrypt ha incorporado soporte de promesa, por lo que en lugar de envolver la devolución de llamada con su propia promesa, puede simplemente await bcrypt.hash(pass, salt) y obtendrá la contraseña cifrada, luego su Promise.resolve('') funcionará como lo pretendes también. Tampoco veo la necesidad de then((onFullfilled: string) => onFullfilled) , pero eso desaparecería si elimina la promesa personalizada de todos modos.
En general, intente ceñirse a un único enfoque asíncrono. Todas las devoluciones de llamada (ya no se sugieren), todas las promesas (mejor) o todas las asincrónicas/en espera (prácticamente el estándar ahora).