이메일인증 테스트코드 작성하기

김재현·2024년 1월 14일
1

TIL

목록 보기
74/88
post-thumbnail

TDD는 아니지만, GitHub Action으로 CI/CD가 적용되어있기에 그 이점을 살리고자 테스트코드를 작성하기로 했다.

UserService의 테스트코드를 작성하며 다른 부분은 문제가 없었지만, 이메일을 보내는 것과 redis에 저장하는 것 등은 테스트코드를 어떻게 해야할까?

각 Service를 별개로 테스트하기

그것은 바로 '이메일은 보내는 것'과 'Redis에 저장되는 것'은 UserService와 별개의 Service라고 보고 무시하는 것이다.

userService.singup()을 했을 때, 그러한 동작을 무시하게 하려면 어떻게 해야할까!?

서칭하다보니 doNothing()이라는 친구가 보였다.

doNothing(): 특정 메서드가 호출되었을 때 아무런 동작을 하지 않도록 설정하는 메서드

마침 비동기 처리를 했기 때문에 이것을 적용하기 더 쉬울 것이라 판단하고 바로 적용해보았다.

UserService

위에서 얘기한 무시가 필요한 코드

				...

// 인증번호 메일 보내기
String sentCode = emailAuthService.sendVerificationCode(email);

// redis에 저장하여 5분 내로 인증하도록 설정
emailAuthService.setSentCodeByLoginIdAtRedis(loginId, nickname, email,
    passwordEncoder.encode(password), firstPreferredCategoryId, secondPreferredCategoryId,
    sentCode);

인증번호 메일 보내는 코드는 doNothing()으로 처리하는데 어려움이 없었다.

하지만 redis의 경우에는 조금 문제가 있었다.

EmailAuthService

여기서 아래의 코드를 mock해야되는 상황이다.

redisTemplate.opsForValue().set(loginId, sentCode, 5 * 60 * 1000, TimeUnit.MILLISECONDS);

위의 코드가 void를 return 하므로 아래와 같이 작성했다.

EmailAuthServiceTest

doNothing().when(redisTemplateMock.opsForValue())
	.set(TEST_USER_LOGINID, "sent-code", anyLong(), any(TimeUnit.class));

하지만 에러메세지가 올라왔다.

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at com.example.jujuassembly.domain.user.service.UserServiceTest.signup(UserServiceTest.java:102)

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, which is not supported
 3. you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed

void인데 왜 return이 필요하다는거야... 도대체가 알 수가 없다.

해결 과정

한참을 헤메고 다닌 결과, 어느 블로그 글을 통해 'stack overflow'의 글을 찾을 수 있었다.

redis template를 mock하는데 어려움을 겪는다는 글이었다.

글의 답변으로는 아래와 같은 코드가 달렸다.

@Mock
RedisTemplate<String, String> redisTemplate;

@Mock
private ValueOperations valueOperations;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);
    Mockito.doNothing().when(valueOperations).set(anyString(), anyString());
}

ValueOperations?? 저게 도대체 무엇인가?

valueOperations: RedisTemplate에서 opsForValue()를 호출하여 얻어진 ValueOperations 객체에 대한 모의체(Mock)

"와! 그렇다면 내가 그동안 redis template의 mock을 잘못하고 있었구나!"를 깨달으며 내 코드에 저 방식을 적용해보았다.

mailAuthServiceTest

아래와 같이 코드를 수정하니 테스트코드 정상적으로 동작하며... 꿈에 그리던 초록빛을 볼 수 있었다.

    when(redisTemplateMock.opsForValue()).thenReturn(valueOperations);
    doNothing().when(valueOperations).set(anyString(), anyString(), anyLong(), any(TimeUnit.class));

선배 개발자님들 존경하고 감사하고 사랑합니다💛

출처

관련 포스팅

Previous Post

profile
I live in Seoul, Korea, Handsome

0개의 댓글