멘질멘질] 2023 졸업 프로젝트

JPA Repository Test] @SpringBootTest vs @DataJpaTest

나른한 찰리 2023. 6. 9. 17:01
반응형

 

고민 내용

  • Spring Data JPA 기반의 Repository를 테스트 할 때, @SpringBootTest  @DataJpaTest 중 어떤 어노테이션을 사용하는 것이 좋을까?

 

소스 코드: ChatBotRoomRepositoryTest

  • 시간 차이를 보다 자세히 확인하기 위해, BeforeEach에서 9996개의 ChatBotRoom 데이터를 DB에 생성한 다음, 테스트를 수행하였다.
  • SpringBootTest 어노테이션을 사용하는 경우, @SpringBootTest와 @Transactional를 사용하고, @AutoConfigureTestDatabase와 @DataJpaTest를 주석 처리한다.
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)    // To use MySQL
@ActiveProfiles("test")
@DataJpaTest
//@SpringBootTest
//@Transactional
class ChatBotRoomRepositoryTest {

    @Autowired
    private ChatBotRoomRepository chatBotRoomRepository;

    @BeforeEach
    void setUp() {
        List<ChatBotRoom> rooms = new ArrayList<>();
        for (int i = 5; i <= 10000; i++) {
            ChatBotRoom room = createChatBotRoom("room-" + i, "i" + i, "r" + i);
            rooms.add(room);
        }
        chatBotRoomRepository.saveAll(rooms);
    }

    @Test
    @DisplayName("roomId로 챗봇 대화방을 조회한다")
    void findChatBotRoomByRoomId() {
        // given
        String targetRoomId = "room-1";
        ChatBotRoom room1 = createChatBotRoom(targetRoomId, "i1", "r1");
        ChatBotRoom room2 = createChatBotRoom("room-2", "i2", "r2");
        chatBotRoomRepository.saveAll(List.of(room1, room2));

        // when
        Optional<ChatBotRoom> result = chatBotRoomRepository.findChatBotRoomByRoomId(targetRoomId);

        // then
        // 조회 검증
        assertThat(result.get().getRoomId()).isEqualTo(targetRoomId);
    }

    @Test
    @DisplayName("사용자 닉네임으로 방 전체를 조회한다.")
    void findAllByInitiatorNickname() {
        // given
        String targetNickname = "i1";
        ChatBotRoom room1 = createChatBotRoom("room-1", targetNickname, "r1");
        ChatBotRoom room2 = createChatBotRoom("room-2", targetNickname, "r2");
        ChatBotRoom room3 = createChatBotRoom("room-3", "i2", "r2");
        chatBotRoomRepository.saveAll(List.of(room1, room2, room3));

        // when
        List<ChatBotRoom> rooms = chatBotRoomRepository.findAllByInitiatorNickname(targetNickname);

        // then
        assertThat(rooms).hasSize(2)
                .extracting("roomId", "initiatorNickname", "recipientNickname")
                .containsExactlyInAnyOrder(
                        tuple("room-1", targetNickname, "r1"),
                        tuple("room-2", targetNickname, "r2")
                );
    }

    @Test
    @DisplayName("roomId로 채팅방을 삭제한다")
    void deleteChatBotRoomByRoomId() {
        // given
        String targetRoomId = "room-1";
        ChatBotRoom room1 = createChatBotRoom(targetRoomId, "i1", "r1");
        ChatBotRoom room2 = createChatBotRoom("room-2", "i2", "r2");
        chatBotRoomRepository.saveAll(List.of(room1, room2));

        // when
        chatBotRoomRepository.deleteChatBotRoomByRoomId(targetRoomId);

        // then
//        assertThat(chatBotRoomRepository.findAll().size()).isEqualTo(1);
        assertThat(chatBotRoomRepository.findChatBotRoomByRoomId(targetRoomId)).isEmpty();
    }


    private ChatBotRoom createChatBotRoom(String roomId,
                                          String initiatorNickname,
                                          String recipientNickname) {
        return ChatBotRoom.builder()
                .roomId(roomId)
                .initiatorNickname(initiatorNickname)
                .recipientNickname(recipientNickname)
                .build();
    }
}

 

 

실행 결과

@SpringBootTest & @Transactional

 

 

@DataJpaTest

 

  • 약 300ms 정도 속도로 DataJpaTest가 빠른 것을 확인할 수 있다.
  • 여기서는 테스트 메서드가 3개이고 데이터가 약 10,000개였으나, 추후 테스트 메서드와 데이터가 모두 많아진다면, 시간 차이가 훨씬 커질 것으로 예상할 수 있다.

 

내가 생각한 차이의 원인

  • https://docs.spring.io/spring-boot/docs/1.4.2.RELEASE/reference/html/boot-features-testing.html
  • 위의 공식 문서를 참고하였음.
  • @DataJpaTest can be used if you want to test JPA applications. By default it will configure an in-memory embedded database, scan for @Entity classes and configure Spring Data JPA repositories. Regular @Component beans will not be loaded into the ApplicationContext.
  • Spring Boot provides a @SpringBootTest annotation which can be used as an alternative to the standard spring-test @ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication.
  • 정리하면, @SpringBootTest 는 @ComponentScan 내의 모든 Bean 객체가 ApplicationContext에 올라간후 실행된다.
  • 반면, @DataJpaTest 는 Entity와 JPA Repository와 관련된 것들만 ApplicationContext에 올라간 후 실행된다. 또한 기본적으로 In-memory인 h2-database 기반으로 동작한다.

 

 

결론

  • RepositoryTest의 경우, @SpringBootTest 보다 @DataJpaTest 를 사용하는 것이 성능상에서 조금 더 유리하다. 왜나하면 ApplicationContext의 모든 Bean 객체를 올리지 않기 때문이다.
반응형