인프런 커뮤니티 질문&답변

이진호님의 프로필 이미지
이진호

작성한 질문수

@DataJpaTest에서 참조하는 Entity 관련 질문 있습니다

작성

·

79

0

개발자 선배님들 안녕하세요. @DataJpaTest 관련해서 궁금한점이 생겨서 질문드립니다.

 

제가 작성한 User Entity에서 Address Entity를 FK로 참조하고 있습니다.

 

여기서 UserRepository의 테스트 코드를 작성할 때

 

AddressRepository도 @Autowired로 등록 후 Address를 먼저 save()로 insert 후에 User Insert 테스트를 작성해야 하나요???

 

이러면 UserRepository의 테스트가 아니라 UserRepository, AddressRepository 두개의 테스트를 작성하는 것이라 좀 이상하다고 생각이 드네요..

 

제가 작성한 코드입니다.

 

package com.kh.bookfinder.repository;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import com.kh.bookfinder.entity.Address;
import com.kh.bookfinder.entity.User;
import com.kh.bookfinder.entity.UserStatus;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;

@ActiveProfiles("test")
@DataJpaTest
@TestPropertySource(locations = "classpath:application-test.properties")
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class UserRepositoryTest {

  @Autowired
  UserRepository userRepository;
  @Autowired
  AddressRepository addressRepository;

  Address givenAddress;

  @BeforeEach
  public void setup() {
    givenAddress = Address.builder()
        .si("서울특별시")
        .gu("강남구")
        .dong("역삼동")
        .roadFullAddress("역삼역")
        .build();
    addressRepository.save(givenAddress);
  }

  @Test
  public void userInsertTest1() {
    // Given: User가 주어진다.
    User givenUser = User.builder()
        .email("user@mail.kr")
        .password("password")
        .phone("010-1234-5678")
        .nickname("nickname")
        .status(UserStatus.ADMIN)
        .address(givenAddress)
        .build();

    // When: save() 메서드를 호출하여 User를 저장한다.
    User actual = this.userRepository.save(givenUser);

    // Then: actual은 givenUser와 같다.
    assertThat(actual).isNotNull();
    assertThat(actual).isEqualTo(givenUser);
  }

  @Test
  @DisplayName("email은 unique로 중복이 불가능하다")
  public void userInsertTest2() {
    // Given: email이 같은 User가 2개 주어진다.
    User givenUser1 = User.builder()
        .email("user@mail.kr")
        .password("password")
        .phone("010-1234-5678")
        .nickname("nickname")
        .status(UserStatus.ADMIN)
        .address(givenAddress)
        .build();
    User givenUser2 = User.builder()
        .email("user@mail.kr")
        .password("password2")
        .phone("010-5678-1234")
        .nickname("nickname2")
        .status(UserStatus.ADMIN)
        .address(givenAddress)
        .build();

    // When: save() 메서드를 호출하여 givenUser1를 저장한다.
    User actual = this.userRepository.save(givenUser1);
    // And: save() 메서드를 호출하여 givenUser2를 저장한다.
    // Then: Exception이 발생한다.
    assertThatThrownBy(() -> this.userRepository.save(givenUser2))
        .isInstanceOf(DataIntegrityViolationException.class)
        .hasMessageContaining("Duplicate");
  }

  @Test
  @DisplayName("nickname은 unique로 중복이 불가능하다")
  public void userInsertTest3() {
    // Given: email이 같은 User가 2개 주어진다.
    User givenUser1 = User.builder()
        .email("user@mail.kr")
        .password("password")
        .phone("010-1234-5678")
        .nickname("nickname")
        .status(UserStatus.ADMIN)
        .address(givenAddress)
        .build();
    User givenUser2 = User.builder()
        .email("user2@mail.kr")
        .password("password2")
        .phone("010-5678-1234")
        .nickname("nickname")
        .status(UserStatus.ADMIN)
        .address(givenAddress)
        .build();

    // When: save() 메서드를 호출하여 givenUser1를 저장한다.
    User actual = this.userRepository.save(givenUser1);
    // And: save() 메서드를 호출하여 givenUser2를 저장한다.
    // Then: Exception이 발생한다.
    assertThatThrownBy(() -> this.userRepository.save(givenUser2))
        .isInstanceOf(DataIntegrityViolationException.class)
        .hasMessageContaining("Duplicate");
  }
}

 

읽어주셔서 감사합니다..!

답변 1

0

안녕하세요, 인프런 AI 인턴입니다.

이진호님의 질문에 답변드리겠습니다. @DataJpaTest는 주로 JPA 관련 설정만 로드하여 레포지토리 레이어의 컴포넌트에 대한 테스트를 집중적으로 수행할 때 사용됩니다. 따라서 UserRepository의 테스트 코드를 작성할 때 필요한 Address 엔티티를 먼저 저장하는 것이 올바른 접근입니다. 이는 AddressRepository와 UserRepository를 동시에 테스트하는 것이 아니라, UserRepository 테스트에 필요한 사전 조건을 충족시키기 위한 준비 과정으로 볼 수 있습니다. 실제 애플리케이션에서도 사용자를 저장하기 전에 주소 정보가 미리 존재해야 하는 경우가 많으므로, 이러한 테스트 설정은 실제 사용 사례를 잘 반영하고 있습니다.

또한, @DataJpaTest는 기본적으로 내장된 인메모리 데이터베이스를 사용하여 테스트를 실행하지만, @AutoConfigureTestDatabase(replace = Replace.NONE) 어노테이션을 사용하여 실제 데이터베이스에 대한 테스트를 진행할 수도 있습니다. 이 경우 실제 DB 환경을 그대로 사용하여 데이터가 잘 삽입되고 관계가 올바르게 매핑되는지 확인할 수 있습니다.

따라서, 이진호님이 작성하신 테스트 코드는 굉장히 타당하며 User 엔티티와 연관된 Address 엔티티를 먼저 저장하는 접근 방식은 적절합니다.

읽어주셔서 감사합니다.

이진호님의 프로필 이미지
이진호

작성한 질문수

질문하기