프로젝트/뉴스피드

[통합 테스트/MockMvc] 어쩌다 회원 가입만 290번

writtenbyrla 2024. 2. 23. 11:06

 

 

처음 해보는 통합테스트. 개발용 DB의 영향을 최소화하고자 테스트용 DB를 따로 만들어 연결하고 테스트를 진행하였다.

 

 

문제 상황


1. [ DB 연결 문제]

개발용 DB를 MySQL로 사용하고 있기 때문에 혹시나 영향이 갈까 봐 H2를 이용하여 테스트용 DB를 분리하였다. 하지만 H2에서는 user를 예약어로 사용하고 있어 User 엔티티 생성에 있어 문제가 생겼고 MySQL로 변경하여 테스트용 스키마를 별도 생성해서 연결해 주었다.

 

 

2. [ Primary Key 자동생성 문제 ]

@BeforeEach 어노테이션으로 테스트 실행시마다 유저를 생성하여 테스트를 진행하고자 했는데, 언젠가부터 처음에 통과하던 테스트 코드도 다시 실행해 보면 다 유저정보를 찾을 수 없다는 오류가 뜨고, 나중에는 결국 모두 실패하는 상황에 이르고 말았다.

    @BeforeEach
    void setup() {
    	User user = new User("name", "pwd", "email");
    	userRepository.save(user);
    }

 

 

 

@Transactional로 테스트 시에만 DB 가 유효하게 해 놓은 상태라 확인을 해볼 수 없었는데 트랜잭션을 풀고 테스트를 해보니 user_id가 293까지 올라가 있었다.

(계속 테스트 실패한다고 이 방법 저 방법 코드 고쳐보고 시도해 본 게 290여 번...? 진짜 광기...)

 

 

 

 

 

코드를 보니 공통적으로 userDetail에서 userId를 찾아 넘길 때만 테스트 코드가 실패한다.

이유는 모든 테스트코드 할 때마다 user를 지우고 새로 만들고 하니 시퀀스가 자동생성되어 계속 증가하고 있었는데, 나의 테스트 코드에서는 샘플 데이터 유저 한 명(user_id가 1인 사람)을 기준으로 계속 테스트를 하고 있어서 userId가 일치하지 않기 때문에 테스트 코드를 통과하지 못한 것이었다.

 

 

 

 


 

해결 방법


1.  [테스트용 DB에 샘플 데이터 세팅]

모든 경우의 수를 고려하여 미리 세팅해 두고 고정된 DB로 테스트하였다. 

근데 이게 진짜 테스트를 하는 취지에 적합한가?라고 생각했을 때는 잘 모르겠다. DB에 영향을 받지 않고 테스트할 다른 방법을 찾아야겠다. 통합테스트 말고 단위테스트도 시도해 봐야겠다.

 

결국 단위테스트도 시도해 보았으나 또 우여곡절을 많이 겪어 정리해 두었다.

 

[단위 테스트/Mockito] 게시글 서비스 레이어 단위 테스트 - 1 (with ReflectionTestUtils)

MockMvc를 이용하여 컨트롤러 통합테스트를 끝냈다. 테스트를 위한 별도 DB를 생성하여 모든 예외사항에 대해 전체 테스트를 끝냈는데, 아무리 생각해도 비즈니스 로직은 서비스단에 포함되어 있

writtenbyrla.tistory.com

 

 

 

2. [ MySQL dialect 이용하여 TRUNCATE로 초기화 후 테스트 진행 ]

 

더 간단한 방법이 있었다.

 

내가 맞닥드린 문제의 핵심은 @BeforeEach로 더미 데이터를 생성할 경우 @Transactional을 설정하면 매 테스트가 끝날 때마다 ROLLBACK이 진행된다. ROLLBACK시에는 PK가 이전 데이터 PK의 다음으로 저장되기 때문에 ID값으로 조회하여 테스트 하는 경우에는 유저 정보를 찾지 못하는 경우가 생기는 것이다.

 

따라서 ROLLBACK이 아닌 TRUNCATE를 사용하게 되면 테스트 사이사이에 생성됐던 DB를 삭제하게 되므로 PK의 영향을 받지 않을 수 있다. 아래와 같이 스프링 부트에서 test 아래 경로에 sql 파일을 생성하여 아래 코드를 넣어 초기화를 시켜주고 테스트 파일에 @Sql("classpath:~)와 같이 어노테이션을 붙여주면 모든 테스트 실행시마다 다시 기존 DB를 삭제하고 다시 생성하는 것이 반복되므로 PK의 시퀀스 문제가 해결된다고 한다.

 

이미 모든 컨트롤러에 대한 통합 테스트를 마친 이후라 적용해보진 않았지만 어차피 테스트코드에 대한 리팩토링도 해야 하니 그때 적용해 볼 생각이다.

SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE multimedia;
TRUNCATE TABLE comment;
TRUNCATE TABLE comment_like;
TRUNCATE TABLE follow;
TRUNCATE TABLE pwd_history;
TRUNCATE TABLE post;
TRUNCATE TABLE post_like;
TRUNCATE TABLE user;
SET FOREIGN_KEY_CHECKS = 1;

 

 

 

 

 


 

++ [테스트용 DB 분리 방법]

 

개발용 DB와 세팅하는 방법은 유사하다.

개발용 DB는 main-resources-application.yml에 DB  정보를 세팅하는 것이라면 테스트용 DB는 test 아래에 두면

테스트시에만 유효한 DB 정보를 세팅할 수 있다. (내용은 같음, url에 테스트용 DB 별도 스키마 이름을 넣어주면 된다.)

DB 연결 정보는 반드시 gitignore를 통해 깃에 올라가지 않도록 주의 또 주의하기!

 

++ [테스트 관련 해볼 일]

  • DB에 영향을 받지 않고 테스트해보기
  • 비즈니스 로직 단위테스트 해보기
  • 중복되는 테스트코드 분리하여 재사용하는 방향으로 리팩토링 해보기