* 그냥 제 생각대로 만들어서 해결했습니다.
정답x
고민
회원 가입 로직을 구현하던 중 Private 메소드에 대한 테스트를 어떻게 진행해야 할 지에 대한 고민이 생겼다.
외부에서 접근하지 않는 메소드로 굳이 Public으로 작성할 이유가 없었기 때문이다.
실제 문제:
이메일 인증을 완료하고 회원 가입을 승인해주는 로직에서 이메일 인증을 보내기 전에 데이터베이스에 같은 이메일이 존재하는 지 확인하고 존재하지 않는다면 인증 코드를 보내고 아니면 예외를 발생시켜야 했다.
이 때 이메일 중복 확인 메소드를 Private으로 설정했고 테스트 코드를 작성할 때 이메일 중복 확인이 잘 되는지 확인하고 싶었다.
코드:
AuthService
private void checkDuplicatedEmail(String email) {
Optional<Admin> admin = adminRepository.findByEmail(email);
if (admin.isPresent()) {
log.debug("Admin Email duplicated");
throw new BusinessLogicException(ExceptionCode.ADMIN_DUPLICATED);
}
}
같은 이메일을 가진 admin을 Optional 타입으로 데이터베이스에서 깨내고 isPresent()로 존재한다면 예외를 발생시키는 메소드이다.
이에 대한 테스트를 진행하고 싶었다.
테스트 코드
@Test
void 이메일_중복_확인(){
Admin admin = Admin.builder().username("test").loginId("test").password("test").email(toEmail).build();
adminRepository.save(admin);
Assertions.assertThrows(BusinessLogicException.class, () -> {
checkDuplicatedEmail(toEmail);
});
}
될리가 없다. Private 메소드를 외부 클래스에서 깨낼 수 없기 때문이다.
Private 메소드를 굳이 테스트를 위해서 Public으로 바꾸고 테스트하고 다시 바꾸는 것은 의미가 없다고 생각하여
위 checkDuplicatedEmail 메소드를 테스트 코드에 똑같은 형태로 생성하고 테스트하였다.
여기서 의문이 생겼다.
과연 이메일 인증 코드 생성처럼 데이터베이스와 관련없는 로직이 아닌데 똑같은 형태라도 이 메소드가 통과한 것이 실제 로직에 있는 메소드가 통과한 것과 같을까?
그래서 다른 해결 방법은 없는지 조사해 보았다.
해결 방법
- Java Reflection API를 이용한 메소드 호출
- Java Reflection을 이용하여 정적으로 고정된 메소드의 코드를 Method 객체로 뽑아낼 수 있어서 이를 이용하여 직접 호출을 가능하게 한다. -> Proxy 객체를 생성할 때 방식과 비슷하다.
- 방식이 번거롭다.
- Spring의 ReflectionTestUtils를 이용한 메소드 호출
- 스프링 프레임워크의 유틸성 클래스인 ReflectionTestUtils를 사용하여 invokeMethod를 통해 target 클래스의 메소드를 호출할 수 있다.
- 테스트하지 않기
- Private 메소드를 테스트한다는 것 자체가 뭔가 오류가 있다는 것으로 리팩토링을 하라는 경고와 같다고 한다.
결론
Private 메소드는 테스트하는 것을 지양해라!
사실 private 메소드 자체가 클라이언트와 결합도를 낮춰 보안성을 높이는 것인데, 클라이언트 역할을 대신해주는 테스트 코드가 private 메소드 자체에 접근하는 것이 모순적이다.
또한 위 Reflection 방법들은 이 컴파일 에러를 유발하지 못하기 때문에 최대한 사용하지 않는 방향으로 가는 것이 옳다.
이러한 이유로 checkDuplicatedEmail 메소드는 AuthService의 sendMail을 테스트하기 전에 같은 이메일을 가진 admin을 넣어서 해당 메소드가 잘 호출되는 지 확인하는 것으로 결론지었다.
코드
@BeforeEach
void 유저_넣기(){
Admin admin = Admin.builder()
.username("test")
.loginId("test")
.email("test@test.com")
.password("test1234")
.build();
adminRepository.save(admin);
}
@Test
void 이메일_중복_체크(){
assertThrows(BusinessLogicException.class, () ->{
authService.sendCodeToEmail(toEmail);
});
}
예외가 발생하고 테스트가 통과한다.
이메일이 다르다면?
예외가 발생하지 않아 테스트가 실패한다.
사실 멀리서 보면 별거 아닌 문제였던거 같은데 너무 메소드를 하나하나 분리하여 테스트하려고 했던 것 같다.
참고
1. https://mangkyu.tistory.com/235
'Dev > Spring' 카테고리의 다른 글
Spring Cloud Eureka Swagger 연결하기 (2) | 2024.04.15 |
---|---|
JWT 리프레시 토큰 Cookie에 저장하기 (1) | 2024.03.28 |
JWT 사용 이유와 정리 (1) | 2024.03.11 |
세션과 CORS 문제 (0) | 2024.03.10 |
스프링 시큐리티 Session 인증 + React 로그인 문제 (0) | 2024.03.10 |