signUp service code
@Transactional()
async signUp(signUpRequestDto: SignUpRequestDto): Promise<any> {
const { email, name, password } = signUpRequestDto;
const user = await this.userRepository.findOneUserByEmailLockMode(email);
if (user) {
throw new ConflictException('The email is already in use');
}
const saltRounds = this.configService.get<string>('SALT_ROUNDS');
if (saltRounds === undefined) {
throw new Error('SALT_ROUNDS is not defined in the configuration.');
}
const hashedPassword = await bcrypt.hash(password, parseInt(saltRounds));
const newUserEntity = new User({ name, email, password: hashedPassword });
const newUser = await this.userRepository.signUp(newUserEntity);
return new SignUpResponseDto({ ...newUser });
}
고려해야 할 것 들이 많아서 한참 고생을 했다.
다음에 참고하기 위해서 기록을 남김
목적
- signUp 안에 구현된 코드들이 원하는대로 작동하는 가만 확인하기
테스트시 확인해야 하는 사항들
- configService.get
- 'SALT_ROUNDS' 를 정상적으로 받는가?
- undefined 이면 에러를 throw 하는가?
- bcrypt
- password, parseInt(saltRounds) 변수를 받아서 사용하고 있는가?
- hashedPassword 를 잘 반환하는가?
- userRepository
- 중복되는 email 이 있을시 에러를 throw 하는가?
bcrypt mocking
import * as bcrypt from 'bcrypt';
// import 한 함수를 중 사용할 method 를 테스트하기위해서 mocking
jest.mock('bcrypt', () => ({
hash: jest.fn(),
}));
// 값을 반환 받기 위한 코드
jest.spyOn(bcrypt, 'hash').mockImplementation(async () => 'hashedpassword');
transactional 을 사용하고 있기 때문에 이 부분을 mocking
jest.mock('typeorm-transactional', () => ({
Transactional: () => jest.fn(),
initializeTransactionalContext: jest.fn(),
}));
.env 파일을 읽어오는 configureService 를 mocking
const mockConfigService = {
get: jest.fn(),
};
// 원하는 값이 없는 경우
configService.get.mockReturnValue(undefined);
// 원하는 값이 있는 경우
const saltRounds = '10';
configService.get.mockReturnValue(saltRounds);
완성된 코드
import { UserService } from '../application/user.service';
import { UserRepository } from '../domain/user.repository';
import { Test, TestingModule } from '@nestjs/testing';
import { USER_REPOSITORY } from '../../common/const/inject.constant';
import { SignUpRequestDto } from '../dto/signUp.request.dto';
import { ConfigService } from '@nestjs/config';
import { AuthService } from '../../auth/application/auth.service';
import { User } from '../domain/User.entity';
import * as bcrypt from 'bcrypt';
import { ConflictException } from '@nestjs/common';
jest.mock('typeorm-transactional', () => ({
Transactional: () => jest.fn(),
initializeTransactionalContext: jest.fn(),
}));
jest.mock('bcrypt', () => ({
hash: jest.fn(),
}));
const mockUserRepository: jest.Mocked<UserRepository> = {
signUp: jest.fn(),
findOneUserByEmailLockMode: jest.fn(),
findOneUserByEmail: jest.fn(),
findOneUserById: jest.fn(),
softDeleteUser: jest.fn(),
};
const mockConfigService = {
get: jest.fn(),
};
describe('UserRepository', () => {
let userRepository: jest.Mocked<typeof mockUserRepository>;
let userService: UserService;
let configService: jest.Mocked<typeof mockConfigService>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: USER_REPOSITORY,
useValue: mockUserRepository,
},
{
provide: ConfigService,
useValue: mockConfigService,
}
],
}).compile();
userService = module.get<UserService>(UserService);
configService = module.get(ConfigService);
userRepository = module.get(USER_REPOSITORY);
});
describe('signUp', () => {
it('should throw ConflictException if email is already in use', async () => {
const usedEmail = 'useremail@email.com';
const signUpRequestDto: SignUpRequestDto = { name: 'tester', email: usedEmail, password: '12345678' };
const { email, name, password } = signUpRequestDto;
const user: User = new User({ email, name, password });
user.id = 1;
userRepository.findOneUserByEmailLockMode.mockResolvedValue(user);
await expect(userService.signUp(signUpRequestDto)).rejects.toThrow(ConflictException);
});
it('should throw Error if saltRounds is not set in configService', async () => {
const usedEmail = 'useremail@email.com';
const signUpRequestDto: SignUpRequestDto = { name: 'tester', email: usedEmail, password: '12345678' };
const { email, name, password } = signUpRequestDto;
const user: User = new User({ email, name, password });
user.id = 1;
userRepository.findOneUserByEmailLockMode.mockResolvedValue(null);
configService.get.mockReturnValue(undefined);
await expect(userService.signUp(signUpRequestDto)).rejects.toThrow(Error);
});
it('should sign up a new user ', async () => {
const signUpRequestDto: SignUpRequestDto = { name: 'tester', email: 'test@email.com', password: '12345678' };
const { email, name, password } = signUpRequestDto;
const newUser: User = new User({ email, name, password });
newUser.id = 1;
const saltRounds = '10';
userRepository.findOneUserByEmailLockMode.mockResolvedValue(null);
configService.get.mockReturnValue(saltRounds);
jest.spyOn(bcrypt, 'hash').mockImplementation(async () => 'hashedpassword');
userRepository.signUp.mockResolvedValue(newUser);
const result = await userService.signUp(signUpRequestDto);
console.log(result);
expect(bcrypt.hash).toHaveBeenCalledWith(password, parseInt(saltRounds));
});
});
});