Back End/Spring Data JPA

[Spring JPA] 왜 테스트코드와 실제 어플리케이션코드가 실행이 다르게 될까?

DevPing9_ 2021. 12. 24. 17:39

실제 API 호출코드는 아무 문제 없이 잘돌아가는데

 

똑같이 실행될거라 여겨진 테스트코드가

오류를 내뱉거나 쿼리가 예상과는 다르게 작동하는 것을

경험하신분들이라면 잘 찾아 오셨다 🥳🥳🥳

 

원인JUnit 과 Spring Test 에 대한 이해도 부족일 것이다.

모든 테스트 케이스를 다룰 수 없으니 하나만 예시를 들어보겠다.

따라서 해당 현상을 경험하신분이라면 JUnit + Spring Test 에 대해 공부하시는게 좋다.

( 테스트 환경을 실제환경과 동일한 구성을 하기 위해서는 노력이 많이 필요하다. )

 

 

테스트환경 구축방법을 제대로 알지 못한채

테스트 코드를 설계하는 것은 

지옥으로 가는 지름길이라는 것을 알아두자.

(JPA 도 그렇다)

I followed Hibernate ORM to hell and came back alive to tell about it

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ


 

일단 여기까지 오신분들은 영속성이 무엇이고 @Transactional 이 무엇인지는 다들 알고 계실꺼라 확신한다.

 

필자는 그 개념에 따라 테스트코드를 작성하였는데, 예상과 다르게 돌아가서 맞왜틀(맞는데 왜 틀리지..)의 늪에서 허우적이고 있었다.

 

 

[대부분의 원인]

 >>> 테스트코드의 어노테이션을 정확히 몰라서 발생한다.

 

 @DataJpaTest 어노테이션을 보면 아래와 같다. 

 

 

즉, @DataJpaTest 는 @Transactional 을 포함하고 있고

이는 각 테스트(메서드)마다 @Transcational 을 달아준 것과 동일한 기능을 한다.

* 추가정보) @DataJpaTest 는 @Configuration 을 빈으로 불러오지 않는다. (Auditing 이 안되는 경우 해당링크 참조)

 

그리하여 예를 들자면, API 컨트롤러에서 @Transactional 없이 코드를 작성하고,

동일하게 테스트코드를 작성하면 다른결과를 통보받는 것이다.

(운이 좋으면 SQL 호출 횟수만 다를텐데, 운이 나쁘면 Lazy initialization Exception 을 마주할때도 있을 것이다.)

 

아래는 SQL 호출 횟수가 달라지는 예시이다.

 

 

[SQL 호출횟수가 달라지는 현상]

  * Post 객체는 Blog 와 @ManyToOne 연관관계를 맺고 있고, FetchType.EAGER, CascadeType.ALL 이라 가정하겠다.

/* ------------------ Application Code Below ----------------- */

@Controller
public class MyController{
    @Autowired BlogRepository blogRepository;
    
    @Getmapping("/blogs")
    @ResponseBody
    public List<Blog> blogs(){
    	return blogRepository.findAll();
        
        ////// Actual Query ///////
        /* 
        1. blog 를 select 하는 쿼리문 발생
        2. blog 가 가지고 있는 post 를 select 하는 쿼리문 발생
        */
    }
}
/* ------------------ Application Code Above ----------------- */



/* ------------------ Test Code Below ----------------- */
@DataJpaTest
public class PostRepositoryTest(){
    @Autowired
    BlogRepository blogRepository;
    @Autowired
    PostRepository postRepository;
    
    @Test
    void ReadTest(){
    	Blog blog1 = new Blog("공부하는 개발자 Ping9");
        Blog blog2 = new Blog("누워서 일하는 개발자 Hing9");
    	Post post1 = new Post("SQL Injection에 관하여", blog1);
        Post post2 = new Post("Log4j 의 보안 이슈", blog2);
        
        // Cascade.ALL 로 연결되어있다고 가정
        postRepository.save(post1);
        postRepository.save(post2);
        
        ////// Expected Query //////
        /* 1. select blog + 2. select post */
        blogRepository.findAll().forEach(System.out::println);
        
        /////// Actual Query ///////
        /* 1. select blog */
        // 이유 : 같은 트랜잭션임, select 가 나가는 이유는 blog1,2 말고 더 있나 싶어서 날리는 것 일뿐.
        
    }
}

 

 

 


[Reference]

1. Spring Boot Test (NHN Colud) 

 

Spring Boot Test : NHN Cloud Meetup

Spring Boot Test

meetup.toast.com

 

 

728x90