개요


테스트를 진행할 때 곤란한 상황 중 하나는 일정 시간이 지난 후 결과값이 달라지는 것입니다. 물론 우리는 시간의 변화에 따라 실패할 가능성이 있는 테스트를 작성해서는 안 됩니다.

그렇지만 테스트 대상 메서드 실행 시점으로부터 단 1초라도 지났을 때의 반환값과 현재의 반환값이 다름을 검증하고 싶을 때는 어떻게 해야할까요? 가장 먼저 생각나는 방법은 Tread.sleep()을 호출하는 것입니다. 쉽고 간단한 방법이지만 전체 테스트 수행 시간도 덩달아 늘어난다는 문제점이 발생합니다.

이번 게시글에서는 이러한 상황에 유용하게 사용할 수 있는 테스트용 라이브러리 awaitility를 직접 사용해보고 정리해보았습니다.

awaitility란?


awaitility는 비동기 시스템을 테스트할 때 사용할 수 있는 라이브러리입니다. 비동기 메서드를 실행했을 때, 해당 메서드의 반환값을 받기 위해서는 일정 시간 대기할 필요가 있습니다. 다음 예시를 한 번 살펴보겠습니다.

@Test
void updatesCustomerStatus() {
		// 고객의 상태 변화에 관한 비동기 메시지를 발행한다.
    messageBroker.publishMessage(updateCustomerStatusMessage);
    // 고객의 상태 변화가 변경되었음을 확인하는 메서드가 실행될때까지 최대 5초 기다린다.
    await().atMost(5, SECONDS).until(customerStatusIsUpdated());
    ...
}
//출처: <https://github.com/awaitility/awaitility>

이처럼 awaitility는 비동기적으로 발행되는 메시지가 있을 때, 해당 메시지를 소비하는 쪽의 상태가 변경될때까지 대기할 수 있도록 조절하는 방법을 제공합니다. 물론 이 과정에서 테스트 시간은 늘어나지 않습니다.

또한 .atLeast(5, SECONDS)와 같이 시간의 변화에 따라 다른 반환값이 달라지는 상황에도

적용하기


저는 리프레시 토큰을 이용해서 액세스 토큰을 갱신하는 상황을 테스트하고 싶었습니다.

@Test
@DisplayName("성공: 새로운 액세스 토큰이 발행됨")
void refreshAccessToken() {
    //given
    String refreshToken = memberToken.refreshToken();

    //when
    MemberToken newMemberToken = jJwtProvider.refreshAccessToken(refreshToken);

    //then
    assertThat(newMemberToken.accessToken()).isNotEqualTo(refreshToken);
}

문제는 테스트 수행시간이 너무 빠른 나머지 기존의 토큰과 동일한 값을 가진 액세스 토큰이 발행된다는 점이었습니다.

다행히 awaitility는 최소 이만큼 대기하라! 라고 메시지를 전달하는 .atLeast() 메서드를 제공합니다. 해당 메서드를 위 테스트 코드에 적용하면 다음과 같이 변경됩니다.

@Test
@DisplayName("성공: 새로운 액세스 토큰이 발행됨")
void refreshAccessToken() {
    //given
    String refreshToken = memberToken.refreshToken();

    //when
    //then
    await().atLeast(10, TimeUnit.MILLISECONDS).untilAsserted(() ->
    {
        MemberToken newMemberToken = jJwtProvider.refreshAccessToken(refreshToken);
        assertThat(newMemberToken.accessToken()).isNotEqualTo(refreshToken);
    });
}