서론

개발을 진행하여 Repository Test를 진행하다보면, 다른 Entity와의 연관관계가 있는 Entity를 삽입처리해주어야할 떄가 있습니다.

이떄, 해당 연관관계의 Entity를 참조하지않고, 무작정 넣을경우 아래의 에러메세지가 발생합니다.

org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing

 

이때, 개발환경이 MSA 환경일경우 다른 Entity를 가져오기 위해서는 다른 Service와 소통해야하는데 

이럴 경우, 단위테스트의 목적과 맞지 않습니다.

 

그렇기에 간단하게, ManyToOne 의 관계에서 바로 해당 FK로 지명된 Entity를 바로 넣을 수 있는 작업을 통해 테스트를 원활하게 진행하도록 합니다.

또한 이때, 해당 값은 유일성을 보존하도록 설정되어있다면 랜덤값 삽입을 통해, sql constraint 에서 오류가 나지 않도록 설정합니다.

 

본론

해당 Entity 정보입니다.

 

Member.class

@Entity
@ToString(exclude = {"member_seminar_list"})
public class Member extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long member_no;

    @Column(length = 100, nullable = false, unique = true, name = "member_id")
    private String member_id;

    @Column(length = 100, nullable = false)
    private String member_password;

    @Column(length = 100, nullable = true)
    private String member_nickname;

    @Column(nullable = false)
    private boolean member_from_social;

    @OneToMany(mappedBy = "member")
    private List<Member_Seminar> member_seminar_list;


}

 

Member_Seminar.class

@Entity
@ToString(exclude = {"member", "seminar"})
public class Member_Seminar extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long member_seminar_no;

    @ManyToOne(
            targetEntity = Member.class,
            fetch = FetchType.LAZY,
            cascade = CascadeType.ALL
    )
    @JoinColumn(name = "member_id")
    private Member member;

    @ManyToOne(
            targetEntity = Seminar.class,
            fetch = FetchType.LAZY,
            cascade = CascadeType.ALL
            )
    @JoinColumn(name = "seminar_name")
    private Seminar seminar;

}

 

Seminar.class

@Entity
@ToString(exclude = {"member_seminar_list"})
public class Seminar extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long seminar_no;

    @Column(length = 100, nullable = false, unique = true)
    private String seminar_name;

    @OneToMany(mappedBy = "seminar", fetch = FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval = true)
    private List<Member_Seminar> member_seminar_list;

}

 

위의 Entity가 세팅되었습니다.

 

이제 Member_Seminar에 Test진행시에 Data를 삽입할때, 자동으로 Member Entity와 Seminar Entity의 값도 같이 삽입되게하기 위하여

아래와 같이 ManyToOne 관계의 옵션값에

@ManyToOne(
        targetEntity = Member.class,
        fetch = FetchType.LAZY,
        cascade = CascadeType.ALL
)
@JoinColumn(name = "member_id")
private Member member;
  • targetEntity: 참조할 엔터티의 클래스 타입을 지정합니다. Member.class 타입을 참조하는 것으로 설정했습니다.
  • fetch: 데이터 로딩 전략을 설정합니다. FetchType.LAZY는 지연 로딩을 의미하며, 데이터가 실제로 필요한 시점에 로딩됩니다.
    • FetchType.EAGER는 즉시 로딩을 의미하며, 엔터티가 조회될 때 관련 데이터도 함께 로딩됩니다.
  • cascade: 연관된 엔터티에 대한 영속성 작업 전파 전략을 설정합니다. 여기서 CascadeType.ALL은 모든 영속성 작업을 연관된 엔터티에도 전파하겠다는 의미입니다. 예를 들어, 부모 엔터티가 저장되면 자식 엔터티도 함께 저장됩니다.
  • @JoinColumn(name = "member_id"): 외래 키(Foreign Key)를 정의하는데 사용됩니다. name 속성은 외래 키 컬럼의 이름을 지정합니다. 이 경우 "member_id" 컬럼이 외래 키 역할을 하게 됩니다.
  •  
  • 이러한 설정을 통해 Member_Seminar 엔터티가 Member 엔터티와 다대일 관계를 가지며, member_id 컬럼을 통해 두 엔터티 간의 관계가 맺어집니다.

위의 로직을 각 엔티티에 넣음으로써 해결할 수 있습니다.

 

 

저같은경우 @BeforeEach를 활용하여 각 테스트 전에 해당 테스트 정보가 있는지 확인하고, 데이터가 없다면 stub을 생성하여 직접 삽입합니다.

MemberSeminarRepositoryTest

private final Long member_seminar_no = 1L;
private final String member_id = "passionfruit200@hello.world"+ UUID.randomUUID();
private final String member_password = "password";
private final String seminar_name= "SeminarTest" + UUID.randomUUID();

@BeforeEach
public void setup() throws Exception{
    if(memberSeminarRepository.findAllByMember_id(member_id).isEmpty()){

        // Member와 Seminar를 모방하는 Stubs/Mocks 생성
        Member memberStub = Member.builder()
                .member_id(member_id)
                .member_password(member_password)
                .build();

        Seminar seminarStub = new Seminar();
        seminarStub.setSeminar_name(seminar_name);

        // CREATE Member_Seminar
        Member_Seminar member_seminar = Member_Seminar.builder()
                .member(memberStub)
                .seminar(seminarStub)
                .build();

        memberSeminarRepository.save(member_seminar);
    }

}

 

결론

위의 코드르 테스트 진행시에 Dummy 값을 넣음으로써 원활하게 RepositoryTests를 진행할 수 있습니다. 

+ Recent posts