개발일기장

xuni - 시간과 관련된 테스트는 어떻게 하면 좋을까?

자몽포도 2023. 5. 5. 12:11

 

목차

1. 주저리주저리

2. 해결책

-  테스트 환경에서만 시간 조작

valueCreatedTime 를 변경하는 메서드 만들기

- 필드의 접근 레벨 변경 private 에서 protected

3. 3안을 채택한 이유


주저리주저리

종종 시간과 관련된 테스트를 해야할 때가 있다. 이럴 때 참 난감하다. 결국에는 공식 문서까지 살펴볼 때도 있었지만 그런 내용을 잘 포함하고 있지 않은 것 같다. 내 경험을 바탕으로 정리한 시간과 관련된 테스트는 두 종류이다.

 

1. 특정 시간에만 동작해야하는 로직

예를 들면 토요일에 주말 세일을 한다. 토요일에만 세일가가 적용되는지 테스트해야하는 경우가 이에 해당한다. 

 

2. 시간 자체를 검증하는 테스트

예를 들어 인증을 위한 토큰을 발급했다고 해보자. 토큰에는 일반적으로 유효 시간을 설정한다. 현재 토큰이 유효한 시간 내에 있는지를 검증할 필요가 있다.

 

오늘 제가 고민한 부분은 2번과 관련된 내용입니다. 진행하고 있는 프로젝트에서 현재 회원 가입 방식은 이메일입니다.

회원 가입을 하기 위해서는 이메일을 통해 본인 인증을 해야 합니다. 본인 인증을 인증 코드를 통해 진행되고 인증 코드와 관련된 규칙은 아래와 같습니다.

 

여기서 세번째 규칙을 보면 유효 시간을 설정해야 합니다. 이를 구현하는 것 자체는 어려운 일이 아니라고 생각하지만 테스트를 고려하면 조금 고민이 생기게 됩니다. 3번 규칙은 아래와 같이 구현되어 있습니다.

 

 

AuthCode 객체 생성자

인증 코드를 생성하면 인증 코드 값과 인증 코드 값 생성 시간이 초기화됩니다.

 

 

인증 코드 값 검증 메서드

 

이후 인증 코드 유효 시간을 검증해야할 때, 생성 시간을 바탕으로 유효한지 검증합니다. 

 

여기서 문제가 발생합니다. 실제 환경에서 사용자들의 인증 코드 발급과 인증 코드를 검증하는 두 행위는 시간차를 가집니다. 쉽게 어떤 사용자가 인증 코드를 받고 그 즉시 인증하는 것이 아니라 일정 시간이 지난 뒤 인증을 받을 수도 있다는 말입니다. 하지만 테스트는 그렇지 않습니다.

 

인증 코드 생성과 검증이 한 작업 내에서 이뤄집니다. 그렇기 때문에 인증 코드 생성 시간과 검증을 받을 때의 시간은 아주 미세한 차이밖에 존재하지 않습니다.

 

저는 valueCreatedTime 필드를 변경하는 어떠한 메서드도 작성하지 않았습니다. 문제를 어떻게 해결하면 좋을까요?


해결책

1. 테스트 환경에서만 시간 조작

프로덕트 코드에 영향을 주지 않고 테스트를 수행할 수 있으니 가장 이상적인 방법이라고 생각합니다. 하지만 실패했습니다. java.time.Clock  추상클래스를 활용하면 해결할 수 있을 것 같았습니다. 하지만 Junit5에 Extentison을 적용하는 과정에서 실패했습니다. 오늘은 할 일이 많아서 여기에 더 많은 시간을 쏟을 수 없으니 아이디어만 알고 넘어가기로 했습니다.

 

2. valueCreatedTime 를 변경하는 메서드 만들기

protected void setValueCreatedTime(LocalDateTime valueCreatedTime) {
    this.valueCreatedTime = valueCreatedTime;
}

 

객체의 상태를 변경하는 메서드는 필요한 경우에만 만드는 것이 바람직하다고 알고 있습니다. 하지만 이 경우를 필요해서 만든다고 생각하면 안됩니다. 비즈니스 로직 상 필요한 것이 아니라 테스트만을 위해 필요한 메서드이기 떄문입니다.

 

그래도 외부에서 아무렇게나 사용할 수 없다고 접근 제어자를 protected 로 설정하였습니다. 하지만 이것도 만병통치약은 아닙니다. 누군가가 객체 내부에서 public 메서드 내부에 setValueCreatedTime() 을 사용해서 의도치 않게 외부에서 시간을 변경할 수 있도록 만들수도 있가 때문입니다.

 

만약 2번 방식을 채택한다면 메서드 명 혹은 주석을 통해 주의 메시지를 남기는게 좋을 것 같습니다.

 

 

3. 필드의 접근 레벨 변경 private 에서 protected (채택)

저는 이 방법을 채택했습니다. 사실 필드 접근제어자가 private 아니라는 것은 매우매우 어색하고 일반적인 개발 방법이 아니겠지만 이 방법이 그나마 괜찮다고 생각했습니다. 접근 제어자를 private 에서 protected 로 변경했습니다.

protected LocalDateTime valueCreatedTime;

 

이후 테스트 환경에서 클래스를 상속받아 시간을 변경하는 메서드를 만들어 테스트를 수행할 수 있었습니다.

 

 

이제 3번을 채택한 이유로 포스팅을 마무리하겠습니다.


3안을 채택한 이유

우선 이상적인 방법은 1번이지만 능력 이슈로 인해 제외했습니다. 저는 2안, 3안이 가진 잠재적인 위험의 발생가능성을 고려해서 발생가능성이 더 낮다고 판단한 3번을 채택하였습니다.

 

2안, 3안은 아래와 같은 위험성을 내포하고 있습니다.

2안 :  protected 메서드라도 public 메서드 내부에 사용되어 의도치 않게 사용될 수 있다.

3안 : 클래스를 상속받을 경우 해당 클래스는 protected 필드에 접근하여 상태를 자유로이 변경할 수 있다.

 

클래스의 유형마다 다르겠지만 위 클래스는 엔티티입니다. 제가 개발 경험이 적기 때문에 그럴수도 있겠지만 엔티티를 상속받는 경우는 보지 못했습니다. 그래서 3안이 가진 잠재적인 위험이 발생할 가능성이 지극히 낮다고 판단했습니다.