본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.
예전에 Jest에 대해서 배웠을 때 사용한 GIVEN, WHEN, THEN 주석을 활용해서 코드를 구분지음
테스트 자체는 간단하지만 실제로 실행해보진 못해서 제대로 동작하는지는 잘 모르겠음...ㄷㄷ
아마 문제가 생긴하면 toBe()
에서 발생할 것 같은데, 만약 에러가 발생하면 저 부분은 값이 같은지를 비교하는게 아니라 다른 방식으로 테스트할 예정
describe('signUp', () => {
it('should sign up', async () => {
// GIVEN
const signUpResult = {
id: 1,
email: 'test@test.com',
nickname: 'Test',
profile: 'test_profile_image_url',
createdAt: '2024-07-05T23:08:07.001Z',
updatedAt: '2024-07-05T23:08:07.001Z',
};
// WHEN
// 모킹된 서비스의 signUp메서드를 실행하면 signUpResult 값을 반환한다는 의미
mockAuthService.signUp.mockResolvedValue(signUpResult);
// THEN
// 컨트롤러의 실제 signUp 메서드의 결과가 signUpResult와 같은지 확인
expect(await controller.signUp(signUpDto)).toBe(signUpResult);
// 컨트롤러에서 서비스의 sinUp 메서드를 사용할 때 다음과 같은 매개변수를 사용하는지 확인
expect(mockAuthService.signUp).toHaveBeenCalledWith(signUpDto);
});
});
describe('signIn', () => {
it('should return tokens', async () => {
// GIVEN
const signInResult = {
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
};
// 모킹된 서비스의 signIn메서드를 실행하면 signInResult 값을 반환한다는 의미
mockAuthService.signIn.mockResolvedValue(signInResult);
// WHEN
// 실제로 컨트롤러의 메서드를 동작시키는 부분
// 컨트롤러 메서드의 매개변수로 signInDto를 사용
const response = await controller.signIn(signInDto);
// THEN
// 컨트롤러의 실제 signIn 메서드의 결과에 accessToken이 있는지 확인
expect(response).toHaveProperty('accessToken');
// 컨트롤러의 실제 signIn 메서드의 결과에 accessToken이 있는지 확인
expect(response).toHaveProperty('refreshToken');
// 컨트롤러의 실제 signIn 메서드의 accessToken이 문자열인지 확인
expect(typeof response.accessToken).toBe('string');
// 컨트롤러의 실제 signIn 메서드의 refreshToken이 문자열인지 확인
expect(typeof response.refreshToken).toBe('string');
// 컨트롤러에서 서비스의 signIn 메서드를 사용할 때 다음과 같은 매개변수를 사용하는지 확인
expect(mockAuthService.signIn).toHaveBeenCalledWith(signInDto);
});
});
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
// AuthService Mocking
const mockAuthService = {
signUp: jest.fn(),
signIn: jest.fn(),
signOut: jest.fn(),
reissue: jest.fn(),
};
// Auth sign-up DTO
interface IUserSignUp {
email: string;
password: string;
passwordCheck: string;
nickname: string;
profile: string;
}
// 가짜 값 설정
const signUpDto: IUserSignUp = {
email: 'test@test.com',
password: '123123',
passwordCheck: '123123',
nickname: 'Test',
profile: 'test_profile_image_url',
};
// Auth sign-in DTO
interface IUserSignIn {
email: string;
password: string;
}
// 가짜 값 설정
const signInDto: IUserSignIn = {
email: 'test@test.com',
password: '123123',
};
describe('AuthController', () => {
let controller: AuthController;
let service: AuthService;
beforeEach(async () => {
// 테스트 전에 임시 데이터 초기화
jest.clearAllMocks();
jest.resetAllMocks();
jest.restoreAllMocks();
// 가짜 모듈 생성
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
providers: [{ provide: AuthService, useValue: mockAuthService }],
}).compile();
// 만들어진 가짜 모듈에서의 service와 controller를 가져옴
controller = module.get<AuthController>(AuthController);
service = module.get<AuthService>(AuthService);
});
// 테스트 후에 임시 데이터 초기화
afterAll(async () => {
jest.clearAllMocks();
jest.resetAllMocks();
jest.restoreAllMocks();
});
it('should be defined', () => {
expect(controller).toBeDefined();
expect(service).toBeDefined();
});
describe('signUp', () => {
it('should sign up', async () => {
// GIVEN
const signUpResult = {
id: 1,
email: 'test@test.com',
nickname: 'Test',
profile: 'test_profile_image_url',
createdAt: '2024-07-05T23:08:07.001Z',
updatedAt: '2024-07-05T23:08:07.001Z',
};
// 모킹된 서비스의 signUp메서드를 실행하면 signUpResult 값을 반환한다는 의미
mockAuthService.signUp.mockResolvedValue(signUpResult);
// WHEN
// 실제로 컨트롤러의 메서드를 동작시키는 부분
// 컨트롤러 메서드의 매개변수로 signInDto를 사용
const response = await controller.signUp(signUpDto);
// THEN
// 테스트 진행하는 부분
// 컨트롤러의 실제 signUp 메서드의 결과가 signUpResult와 같은지 확인
expect().toBe(signUpResult);
// 컨트롤러에서 서비스의 sinUp 메서드를 사용할 때 다음과 같은 매개변수를 사용하는지 확인
expect(mockAuthService.signUp).toHaveBeenCalledWith(
signUpDto.email,
signUpDto.password,
signUpDto.passwordCheck,
signUpDto.nickname
);
});
});
describe('signIn', () => {
it('should return tokens', async () => {
// GIVEN
const signInResult = {
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
};
// 모킹된 서비스의 signIn메서드를 실행하면 signInResult 값을 반환한다는 의미
mockAuthService.signIn.mockResolvedValue(signInResult);
// WHEN
const response = await controller.signIn(signInDto);
// THEN
// 컨트롤러의 실제 signIn 메서드의 결과에 accessToken이 있는지 확인
expect(response).toHaveProperty('accessToken');
// 컨트롤러의 실제 signIn 메서드의 결과에 accessToken이 있는지 확인
expect(response).toHaveProperty('refreshToken');
// 컨트롤러의 실제 signIn 메서드의 accessToken이 문자열인지 확인
expect(typeof response.accessToken).toBe('string');
// 컨트롤러의 실제 signIn 메서드의 refreshToken이 문자열인지 확인
expect(typeof response.refreshToken).toBe('string');
// 컨트롤러에서 서비스의 signIn 메서드를 사용할 때 다음과 같은 매개변수를 사용하는지 확인
expect(mockAuthService.signIn).toHaveBeenCalledWith(signInDto);
});
});
});
주말 동안 아직 작성하지 못한 컨트롤러 테스트 코드를 작성할 예정
사실 서비스 테스트 코드가 더 중요하지만 지금 당장 서비스 테스트 코드에 매달리는 것보다는 컨트롤러 테스트 코드를 통해서 어느정도 익숙해지고 빠르게 진행할 예정
서비스 테스트 코드는 컨트롤러와는 다르게 실행 성공과 실패 모두를 테스트해야 함
테스트 코드를 바로바로 실행할 수 있는 환경이 아니기에 지금 작성하는 테스트 코드가 제대로된 테스트 코드인지 확인할 수가 없음
일단 머리속으로 최대한 로직을 구상해서 그에 대한 테스트 코드를 구현할 예정
원래는 TDD(Test-Driven Development) 형태로 개발을 진행하고 싶었지만 팀프로젝트의 기간이 짧아서 기능 구현하는 분들이 테스트 코드를 기다리기엔 무리가 있어서 그냥 기능 구현과 테스트 코드가 동시에 진행됨
오늘은 Auth, Board의 컨트롤러 테스트 코드를 작성함
평소 개인 과제에서도 선택이기에 사용해 본적이 거의 없음
그래서 도전한 테스트 코드지만, 생각보다 감이 잡히지 않음
어떤 것을 테스트 해야 하는지?
임의의 값은 어떻게 지정하는 게 좋은지?
어느 정도까지 자세하게 테스트를 해야 하는지?
등의 고민을 가지고 진행하다가 결국 튜터님께 상담을 받음
튜터님께서는 잘 작성하고 있다고 하시지만 아직 팀원들이 코드의 구조를 완성하지 않아서 테스트가 불가능하다보니 눈에 보이는게 없어서 막막함만 있음
결과적으로 튜터님께서는 그런 생각보다는 테스트 코드를 작성하는 사람이 원하는 방향으로 테스트를 진행하고 기능 구현 하는 사람들이 그 테스트 코드에 맞추는 것이라고 말씀하셨음
이렇게 조언을 들어도 감이 잡히지 않는 상태지만, 그래도 어느정도 해답이 나왔으니 이제는 그냥 다른 사람들과 이야기를 통해 쭉 구현하는 일만 남았음