10/19/2023

단위 테스트

발단

사용자 서비스 클래스의 단위 테스트 작성 중 갑자기 마주친 봉변 때문에 멘붕이 왔다... 새로고침 토큰을 설정하고 해제하는 메서드를 테스트하는데 이상하게 새로고 토큰이 갱신이 되지 않았다. 사용자 생성 시 새로고침 토큰은 null이고 setRefreshToken() 메서드를 거치면 Bcrypt를 통해 암호화된 문자열 값으로 새로고침 토큰을 갱신한다. setRefreshToken() 메서드 안에서 update 메서드가 호출된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
// 문제가 발생한 코드! 값에 의한 전달과 참조 타입을 반추한 결과 코드에 문제는 없다!
// 코드를 아무리 수정해도 갱신이 안된다... 당연하지 바보야~
update: async (id, options) => {
  console.log(users); // 사용자가 확실하게 존재한다.
 
  const user = users.find((user) => user.id === id) as User;
  console.log(user); // 근데 undefined?
 
  user.refreshToken = options.refreshToken;
  console.log(user, options);
 
  return;
}
cs

로그를 보니 사용자를 저장하는 사용자 배열에 분명 사용자가 있는데(이상하게 Promise 예약어도 같이 보인다. 뭐지?) user 값이 undefined라니?! JavaScript는 항상 값에 의한 전달(pass by value)만을 지원하기 때문에 객체, 배열과 같은 참조 타입도 원시 타입처럼 값이 복사된다. 즉, 메모리 주소를 포함하는 참조값이 전달된다. 하지만 참조 타입은 원시 타입과 다르게 작동하기 때문에 함수에서 값을 변경하면 그 값이 원본에도 반영된다. 따라서 코드에는 문제가 없다!

실수

몇 십분 삽질을 하다가 이상한 걸 발견했다... 바로 테스트 create() 메서드가 개발 create() 메서드와 달랐다! 구현 자체의 문제가 아니라 개발 create() 메서드는 async 예약어가 없는 동기 함수였는데 테스트 create() 메서드를 프로미스를 반환하는 비동기 함수로 선언했다... 프로미스를 제거하고 테스트를 실행한 결과 새로고침 토큰이 올바르게 갱신되었다! 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 create: (createUserDto: CreateUserDto) => Promise<User>// 망할 문제의 원인... 삽질의 원인!!
 create: async (createUserDto: CreateUserDto) => {
        const user = {
          ...createUserDto,
          id: Math.floor(Math.random() * 10000+ 1,
          refreshToken: null,
        } as User;
 
        return user;
      },
 
 async create(createUserDto: CreateUserDto) {
    try {
      const salt = await bcrypt.genSalt();
      const hashedPassword = await bcrypt.hash(createUserDto.password, salt);
 
      // async가 필요없는 동기 함수!!
      const user = this.usersRepository.create({
        ...createUserDto,
        password: hashedPassword,
      });
 
      return await this.usersRepository.save(user);
    } catch (error) {
      if (error?.errno === MySqlErrorCode.DUPLICATE_ENTRY) {
        throw new ConflictException('이메일 사용 중.');
      }
 
      throw new InternalServerErrorException('사용자 생성 중 오류 발생.');
    }
  }
cs


의문점

하지만 의문점은 아직 남아있다. create() 메서드를 테스트할 때 이러한 문제가 발생하지 않았다. 위의 코드에서 create() 메서드 앞에 await 예약어가 없어도 사용자를 반환하기 때문에 프로미스는 이행되고 이 사용자는 배열에 저장된다. 근데 왜 update() 메서드에서 오류가 발생한 이유가 뭘까? 테스트 update() 메서드 안에서 로그를 찍었을 때 Promise 예약어가 보였다. 검색을 해보니 이행된 상태라는데 왜 Promise 예약어가 보이는지 의문이고 이행된 상태인데 갱신이 안되니 더 의문... 좀 더 알아보자~

update: 2023.10.21

댓글 없음:

댓글 쓰기