Aop, Test 그리고 예외처리

2024. 7. 1. 21:17TIL

✔오늘 배운 중요한 🔑 point

  • Aop 내에서 예외처리를 발생시킬경우에 테스트코드에서 예외처리를 인식하지 못하는 경우가 발생한다.
  • Aop로 validation하는 것보다는 service 로직에서 작성하는 것이 더 바람직하다.

🎯 오늘 배운 내용

 

테스트 코드를 작성 중 내가 예상하지 못한대로 테스트가 실패한 경우가 발생하였다.

 

package com.example.brushuptodolist.domain.authentication.service

import com.example.brushuptodolist.domain.authentication.dto.SignUpRequest
import com.example.brushuptodolist.domain.authentication.jwt.JwtTokenManager
import com.example.brushuptodolist.domain.user.dto.UserRole
import com.example.brushuptodolist.domain.user.entity.User
import com.example.brushuptodolist.domain.user.repository.UserRepository
import com.example.brushuptodolist.infra.redis.VerificationService
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import jakarta.transaction.Transactional
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.test.context.ActiveProfiles

@Transactional
@SpringBootTest
@ActiveProfiles("test")
class AuthenticationServiceTest @Autowired constructor(
    private val userRepository: UserRepository,
    private val jwtTokenManager: JwtTokenManager,
    private val emailService: EmailService,
    private val passwordEncoder: PasswordEncoder,
    private val verificationService: VerificationService,

) {

    private val signUpAndLoginService = SignUpAndLoginService(
        userRepository = userRepository,
        jwtTokenManager = jwtTokenManager,
        passwordEncoder = passwordEncoder,
        emailService = emailService,
        verificationService = verificationService)

    @Test
    fun `비밀번호와 checking비밀번호가 다를경우 예외처리 확인`(){

        //GIVEN

        val request = SignUpRequest(userEmail = "test@naver.com", userPassword = "4321", userName = "요청받은 테스트용 이름1", checkingPassword = "1234")
        // WHEN & THEN

        shouldThrow<RuntimeException> {
            signUpAndLoginService.signUp(request)
        }.let{
            it.message shouldBe "처음에 설정한 비밀번호와 다릅니다!!"
        }

        userRepository.findAll()
            .filter { it.userEmail == "test@naver.com" }
            .let{
                it.size shouldBe 0
            }
        
    }

}

 

 

Expected exception java.lang.RuntimeException but no exception was thrown.

RuntimeException이 발생해야하는데 예외처리가 발생하지 않은 상황이다..

 

현재 나는 비밀번호와 관련된 예외처리나, validation을 AOP로 구현을 한 상태이다

package com.example.brushuptodolist.infra.aop

import com.example.brushuptodolist.domain.api.post.dto.UpdatePostRequest
import com.example.brushuptodolist.domain.authentication.dto.SignUpRequest
import com.example.brushuptodolist.domain.user.repository.UserRepository
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.springframework.stereotype.Component



@Aspect
@Component
class AopAspect(
    private val userRepository: UserRepository
) {

    @Before("@annotation(com.example.brushuptodolist.infra.aop.ValidationSignUp)")
    fun validationSignUpAspect(joinPoint: JoinPoint) {
        val args = joinPoint.args

        val signUpRequest = args.find { it is SignUpRequest } as SignUpRequest

        if (signUpRequest.userPassword == signUpRequest.userEmail) {
            throw RuntimeException("유저 비밀번호와 이메일이 같아서는 안됩니다") 
        }

        if(signUpRequest.userPassword != signUpRequest.checkingPassword){
            throw RuntimeException("처음에 설정한 비밀번호와 다릅니다!!")
        }

        if(userRepository.existsByUserName(signUpRequest.userName)){
            throw RuntimeException("이미 존재하는 유저 이름 입니다!!")
        }

    }
}

 

 if(signUpRequest.userPassword != signUpRequest.checkingPassword){
            throw RuntimeException("처음에 설정한 비밀번호와 다릅니다!!")
        }

해당 코드를 통해서 처음에 설정한 비밀번호가 다르다면 RuntimeException 예외처리가 발생해야하는데 테스트 단계에서는 예외처리가 발생하지 않는 상황.

실제 실행단계에서는 예외처리가 잘 실행이 되는데 왜 테스트 단계에서는 예외처리가 발생하지 않은 것인가..

 

구글링과 튜터님께 조언을 구한 결과 AOP 단계에서 예외 처리가 발생하면, 그 예외가 서비스 메서드의 내부에서 발생한 것이 아니기 때문에, 테스트 코드에서는 해당 예외가 발생하지 않았다고 판단 할 수 있다고 한다.

즉 예외처리가 signUpAndLoginService.signUp에서 발생한 것이 아니라  AopAspect에서 발생했으므로 테스트 코드에서는 예외처리가 발생하지 않았다고 판단하여 테스트가 실패한 것!!

 

정확한 비밀번호인지 확인하는 검증의 로직은 AOP에서 작성하는것 보다는 service 쪽에서 작성하는것이 더 적절하다고 판단하여 ValidationSi대신 service쪽에 작성하는 것으로 수정하였다.

 

테스트가 무사히 성공한 것을 확인할 수있다.

 

🤔 어떻게 활용할까?

aop 내에서 예외처리를 발생시킬경우에 테스트코드에서 예외처리를 인식하지 못할 수도 있으니 aop를 테스트 하는 테스트 코드를 별도로 작성하는 것이 좋다.

📓 오늘의 한줄

"The only true wisdom is in knowing you know nothing."

- Socrates -

 

 

'TIL' 카테고리의 다른 글

Redis Insight 사용법  (0) 2024.07.03
Cache을 이용한 성능개선 프로젝트 (1일차)  (0) 2024.07.02
gitignore로 보안정보 숨기기  (0) 2024.06.30
Connection refused: getsockopt 오류  (0) 2024.06.29
spring Boot에서 Redis 연결하기  (0) 2024.06.28