hanghae99/프로젝트

[테스트코드/통합테스트] 테스트코드 작성 시 겪은 문제와 해결 방법 1탄

욘아리 2024. 2. 25. 19:29

테스트 코드 작성은 개발 과정에서 아주 중요한 부분이라고 할 수 있다. 하지만 처음 테스트 코드를 작성하는 과정에서는 테스트 방법과 다양한 종류에 대한 이해가 필요하다. 나는 그 부분에 대해 이해가 부족했고, Controller를 시작으로 단위테스트를 작성하려고 했으나, 데이터베이스 연동까지 고려하다 보니 결국 통합테스트를 진행하고 있었다. 통합테스트는 테스트용 데이터베이스를 만들어 빈 데이터베이스에서 테스트를 진행했다.

 

❓ 문제 상황

각 테스트 메소드를 개별적으로 실행할 때는 성공했지만, 모든 테스트를 한 번에 실행할 때 실패로 뜨는 현상 발생

다시봐도 눈물난다ㅠㅠㅠ

 

1. @Transactional 어노테이션을 사용하더라도 테스트 중에 사용한 데이터가 실제 데이터베이스에 저장되기 때문에 이로 인해 각 테스트 간의 의존성 문제가 발생하였고, 특히 데이터베이스에서 사용하는 id 값이 설정해 둔 값과 일치하지 않아 실패함.

id 값이 다시 1부터 저장될 것이라고 생각했지만 아니었음

 

2. 로그인 한 유저를 확인할 값(userDetails)으로 MySQL에서 USER 테이블에 저장해 두고 그 값을 setUp해서 사용함.

Long id = 1L;
String email = "1test@test.com";
String encodedPassword = new BCryptPasswordEncoder().encode("qwe123!@#");
String nickname = "1test";
Set<SimpleGrantedAuthority> authorities = Set.of(new SimpleGrantedAuthority("ROLE_USER"));

userDetails = UserDetailsImpl.builder()
        .id(user.getId())
        .email(email)
        .password(encodedPassword)
        .nickname(nickname)
        .authorities(authorities)
        .build();

 

3. 결국 다른 테이블에서도 id값을 가져와야 하는 상황이 발생. 이것 또한 DB에 값을 저장해 두고 사용한다면 빈 데이터베이스를 만들어서 연결한 의미가 없다는 생각이 들었음.

id 23 뭔데..

💡 해결 방법

@BeforeEach 어노테이션을 사용하여 먼저 respository.save(user) 하고, 그 user의 id를 활용하여 userDetails를 생성하는 방법으로 해결했다. 처음에는 id를 지정해 두면서 값을 못 찾아오는 문제가 있었는데 애초에 세팅한 user의 id를 넣어주면 되는 간단한 문제였다. (물론 해결되니까 간단해 보이는 매직)
이렇게 수정함으로써 각 테스트에서 필요한 데이터는 미리 생성하지만, 테스트 간의 의존성 문제를 최소화할 수 있었다. userDetails로 테스트하는 경우뿐만 아니라, 다른 테이블에서도 비슷한 문제가 발생할 때 이러한 방식으로 적용할 수 있었다.

 @BeforeEach
    void setUp() {
        String email = "1test@test.com";
        String encodedPassword = new BCryptPasswordEncoder().encode("qwe123!@#");
        String nickname = "1test";
        Set<SimpleGrantedAuthority> authorities = Set.of(new SimpleGrantedAuthority("ROLE_USER"));

        User user = new User(email, nickname, encodedPassword, UserRoleEnum.USER, true);
        userRepository.save(user);
        user.updatePassword(encodedPassword);

        userDetails = UserDetailsImpl.builder()
                .id(user.getId())
                .email(email)
                .password(encodedPassword)
                .nickname(nickname)
                .authorities(authorities)
                .build();
    }

 

📝 실패한 시도(삽질) 기록

테스트 코드용 데이터베이스로 H2 데이터베이스를 활용하려고 했지만, H2 데이터베이스에서는 "user"가 예약어로 지정되어 있어 User 엔티티가 인식되지 않았다. (이 문제 찾는데도 꽤 걸렸다..ㅎㅎ) 또한, H2 데이터베이스에서 저장된 내용을 시각적으로 확인하기 어려워 답답한 상황이 발생했다.

=> 개발용 데이터베이스로 MySQL을 사용하고 있기 때문에, 테스트용 데이터베이스도 MySQL로 변경하여 일관성을 유지하기로 결정했다. "user" 예약어 관련된 문제도 해결할 수 있었고, MySQL Workbench를 활용하여 실제 데이터베이스 내용을 시각적으로 확인하면서 개발하기로 결정했다.

 

✨👩‍💻 ✨

id값 잘 가져온 이후에는 (자잘한 문제 말고) 큰 문제없이 약 100개 정도의 메소드를 만들었다. 뿌듯하게 멘토링 시간에 피드백을 받았는데 청천벽력 같은 소리를 듣고 말았다..! 내가 놓치고 오해한 부분에 대해선 2탄으로 돌아오겠습니다.....