Daily Logs/TIL (Today I Learned)
const로 선언된 변수는 재할당이 불가능하다.
Jcob.moon
2024. 12. 11. 15:02
프로젝트 마무리 단계에 코드 리펙토링 중
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
import { JwtService } from '@nestjs/jwt';
import { HashingService } from 'src/interface/hashing-interface';
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import {
mockHashingService,
mockJwtService,
mockUserRepository,
} from './__mocks__/mock.auth.service';
import {
loginDTO,
mockPayload,
mockRequest,
mockUser,
signUpDto,
wrongPassword,
} from './__mocks__/mock.auth.data';
import { IUserRepository } from 'src/interface/IUserRepository';
describe('AuthService', () => {
let authService: AuthService;
let jwtService: JwtService;
let hashingService: HashingService;
let userRepository: IUserRepository;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AuthService,
{
provide: 'IUserRepository',
useValue: mockUserRepository,
},
{
provide: 'HashingService',
useValue: mockHashingService,
},
{
provide: JwtService,
useValue: mockJwtService,
},
],
}).compile();
authService = module.get<AuthService>(AuthService);
jwtService = module.get<JwtService>(JwtService);
hashingService = module.get<HashingService>('HashingService');
userRepository = module.get<IUserRepository>('IUserRepository');
});
describe('회원가입', () => {
it('회원가입 시 인증번호가 일치하지 않으면 BadRequestException 던짐', async () => {
await expect(authService.signUp(signUpDto, mockRequest)).rejects.toThrow(BadRequestException);
});
it('회원가입 시 이메일이 중복되면 BadRequestException 던짐', async () => {
mockRequest.session.code = '1234';
mockUserRepository.findByEmail.mockResolvedValue({ email: signUpDto.email });
mockUserRepository.findByNickname.mockResolvedValue(null);
mockUserRepository.findByPhoneNumber.mockResolvedValue(null);
await expect(authService.signUp(signUpDto, mockRequest)).rejects.toThrow(BadRequestException);
expect(mockUserRepository.findByEmail).toHaveBeenCalledWith(signUpDto.email);
});
it('회원가입 시 비밀번호 확인이 다르면 BadRequestException 던짐', async () => {
mockUserRepository.findByEmail.mockResolvedValue(null);
await expect(authService.signUp(wrongPassword, mockRequest)).rejects.toThrow(
BadRequestException,
);
});
it('회원가입 시 닉네임이 중복되면 BadRequestException 던짐', async () => {
mockUserRepository.findByNickname.mockResolvedValue({ nickname: 'nickname' });
await expect(authService.signUp(signUpDto, mockRequest)).rejects.toThrow(BadRequestException);
expect(mockUserRepository.findByNickname).toHaveBeenCalledWith(signUpDto.nickname);
});
it('회원가입 시 전화번호가 중복되면 BadRequestException 던짐', async () => {
mockUserRepository.findByEmail.mockResolvedValue(null);
mockUserRepository.findByNickname.mockResolvedValue(null);
mockUserRepository.findByPhoneNumber.mockResolvedValue({ phoneNumber: '01000000000' });
await expect(authService.signUp(signUpDto, mockRequest)).rejects.toThrow(BadRequestException);
expect(mockUserRepository.findByPhoneNumber).toHaveBeenCalledWith(signUpDto.phoneNumber);
});
it('회원가입 성공 검증', async () => {
mockUserRepository.findByEmail.mockResolvedValue(null);
mockUserRepository.findByNickname.mockResolvedValue(null);
mockUserRepository.findByPhoneNumber.mockResolvedValue(null);
mockHashingService.hash.mockResolvedValue('hashedPassword');
const result = await authService.signUp(signUpDto, mockRequest);
expect(result).toEqual({
message: '회원가입에 성공했습니다.',
});
expect(mockHashingService.hash).toHaveBeenCalledWith(signUpDto.password);
expect(mockUserRepository.save).toHaveBeenCalledWith({
email: signUpDto.email,
password: 'hashedPassword',
name: signUpDto.name,
nickname: signUpDto.nickname,
phoneNumber: signUpDto.phoneNumber,
});
});
});
describe('login', () => {
it('유저가 존재하지 않으면 UnauthorizedException 던짐', async () => {
mockUserRepository.findByEmail.mockResolvedValue(null);
await expect(authService.logIn(loginDTO)).rejects.toThrow(UnauthorizedException);
});
it('비밀번호가 일치하지 않으면 UnauthorizedException 던짐', async () => {
mockUserRepository.findByEmail.mockResolvedValue(mockUser);
mockHashingService.compare.mockResolvedValue(false);
await expect(authService.logIn(loginDTO)).rejects.toThrow(UnauthorizedException);
});
it('유저가 존재하고 비밀번호가 일치하면 엑세스 토큰을 던짐', async () => {
mockUserRepository.findByEmail.mockResolvedValue(mockUser);
mockHashingService.compare.mockResolvedValue(true);
mockJwtService.sign.mockReturnValue(mockPayload);
const result = await authService.logIn(loginDTO);
expect(result).toEqual({
access_token: mockPayload,
});
expect(mockUserRepository.findByEmail).toHaveBeenCalledWith(loginDTO.email);
expect(hashingService.compare).toHaveBeenCalled();
});
});
});
__mocks__.moc.auth.data.ts
export const mockRequest: Request = {
session: { code: 'wrongCode' },
} as unknown as Request;
이런식으로 모킹 데이터를 하나의 파일에 모아서 export 하는 방식으로
테스트 코드를 리펙토링 하고 있였다.
it('회원가입 시 인증번호가 일치하지 않으면 BadRequestException 던짐', async () => {
await expect(authService.signUp(signUpDto, mockRequest)).rejects.toThrow(BadRequestException);
});
인증 번호 불일치 시 에러메세지를 띄우고 그리고 이후는 인증번호가 일치해야하는 경우 이기때문에
export const signUpDto: SignUpDto = {
email: 'test@test.com',
password: 'password',
confirmedPassword: 'password',
name: 'name',
nickname: 'nickname',
phoneNumber: '01000000000',
code: '1234',
};
mockRequest.session.code = '1234';
const 는 참조 값을 고정하고 객체 내부의 속성을 해당 참조값 내부에 저장되어 있으므로 변경이 가능하다는 점을 이용하여
이런식으로 mockRequest.session.code = '1234'; 로 그 이후의 테스트 코드에서 mockRequest 를 참조시
인증번호가 일치하는 상태로 변경하여
중복해서 작성해야하는 경우를 피했다.
요약 :
1. const 원리
const obj = { key: 'value' };
obj.key = 'newValue'; // 가능
console.log(obj); // { key: 'newValue' }
obj = {}; // 불가능: TypeError: Assignment to constant variable
2. 프로젝트에서 활용
export const mockRequest: Request = {
session: { code: 'wrongCode' },
} as unknown as Request;
// 테스트에서 속성 값 수정
mockRequest.session.code = '1234'; // 가능
it('테스트 1', () => {
mockRequest.session.code = '1234'; // 수정
console.log(mockRequest.session.code); // '1234'
});
it('테스트 2', () => {
console.log(mockRequest.session.code); // 여전히 '1234'
});
굉장히 기본적인 이고 이제는 당연할 수 있는 부분이지만 기본적인 부분을 리마인드 하며 적어보았다.