[2024.07.12 TIL] 내일배움캠프 62일차 (심화 플러스 팀프로젝트, 컨트롤러 테스트 코드 구현)

My_Code·2024년 7월 12일
0

TIL

목록 보기
78/112
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.


💻 TIL(Today I Learned)

📌 Today I Done

✏️ 회원가입 컨트롤러 테스트 코드

  • 예전에 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);
    });
  });

✏️ Auth Controller 전체 테스트 코드

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);
    });
  });
});


📌 Tomorrow's Goal

✏️ 컨트롤러 테스트 코드 작성

  • 주말 동안 아직 작성하지 못한 컨트롤러 테스트 코드를 작성할 예정

  • 사실 서비스 테스트 코드가 더 중요하지만 지금 당장 서비스 테스트 코드에 매달리는 것보다는 컨트롤러 테스트 코드를 통해서 어느정도 익숙해지고 빠르게 진행할 예정

  • 서비스 테스트 코드는 컨트롤러와는 다르게 실행 성공과 실패 모두를 테스트해야 함

  • 테스트 코드를 바로바로 실행할 수 있는 환경이 아니기에 지금 작성하는 테스트 코드가 제대로된 테스트 코드인지 확인할 수가 없음

  • 일단 머리속으로 최대한 로직을 구상해서 그에 대한 테스트 코드를 구현할 예정



📌 Today's Goal I Done

✔️ 컨트롤러 테스트 코드 작성

  • 원래는 TDD(Test-Driven Development) 형태로 개발을 진행하고 싶었지만 팀프로젝트의 기간이 짧아서 기능 구현하는 분들이 테스트 코드를 기다리기엔 무리가 있어서 그냥 기능 구현과 테스트 코드가 동시에 진행됨

  • 오늘은 Auth, Board의 컨트롤러 테스트 코드를 작성함

  • 평소 개인 과제에서도 선택이기에 사용해 본적이 거의 없음

  • 그래서 도전한 테스트 코드지만, 생각보다 감이 잡히지 않음

  • 어떤 것을 테스트 해야 하는지?

  • 임의의 값은 어떻게 지정하는 게 좋은지?

  • 어느 정도까지 자세하게 테스트를 해야 하는지?

  • 등의 고민을 가지고 진행하다가 결국 튜터님께 상담을 받음

  • 튜터님께서는 잘 작성하고 있다고 하시지만 아직 팀원들이 코드의 구조를 완성하지 않아서 테스트가 불가능하다보니 눈에 보이는게 없어서 막막함만 있음

  • 결과적으로 튜터님께서는 그런 생각보다는 테스트 코드를 작성하는 사람이 원하는 방향으로 테스트를 진행하고 기능 구현 하는 사람들이 그 테스트 코드에 맞추는 것이라고 말씀하셨음

  • 이렇게 조언을 들어도 감이 잡히지 않는 상태지만, 그래도 어느정도 해답이 나왔으니 이제는 그냥 다른 사람들과 이야기를 통해 쭉 구현하는 일만 남았음


profile
조금씩 정리하자!!!

0개의 댓글