영호
[JPA] 테스트에서 repository.save()해도 createdAt이 null일 때 본문
들어가면서,
repository단위 테스트를 작성하면서 save한 엔티티의 createdAt이 필요했는데, 해당 값이 계속 null인 문제를 해결한 과정입니다.
현재 createdAt은 @EntityListeners(AuditingEntityListener.class) 을 이용해 생성하고 있습니다.
문제 코드
@DataJpaTest
class CafePolicyRepositoryTest {
@Autowired
private CafePolicyRepository cafePolicyRepository;
@Test
void 특정_시간보다_이후에_생성된_카페_리워드_정책이_없으면_빈_리스트_반환() {
Cafe savedCafe = cafeRepository.save(new Cafe());
CafePolicy currentPolicy = cafePolicyRepository.save(new CafePolicy());
System.out.println(currentPolicy.getCreatedAt()); // null
}
}
제가 기대한건 cafePolicyRepository.save()를 하면서 엔티티의 createdAt이 생성되고, 이를 활용하길 원했습니다. 그러나 테스트가 계속 실패했고 원인을 살펴보니 createdAt에 null이 들어있었습니다.
해결 방법
해결방법은 간단합니다. 바로 Test클래스에 엔티티와 마찬가지로 @EntityListeners(AuditingEntityListener.class) 를 붙여주면 됩니다.
@EntityListeners(AuditingEntityListener.class)를 붙여주면 됩니다.
@DataJpaTest
class CafePolicyRepositoryTest {
@Autowired
private CafePolicyRepository cafePolicyRepository;
@Test
void 특정_시간보다_이후에_생성된_카페_리워드_정책이_없으면_빈_리스트_반환() {
Cafe savedCafe = cafeRepository.save(new Cafe());
CafePolicy currentPolicy = cafePolicyRepository.save(new CafePolicy());
System.out.println(currentPolicy.getCreatedAt()); // 정상적으로 값이 들어감
}
}
스택오버플로우를 보면 AuditingEntityListener는 @PrePersist, @PostPersist절에서 실행된다고 합니다. 실제 AuditingEntityListener코드를 살펴보겠습니다.
AuditingEntityListener
@Configurable
public class AuditingEntityListener {
private @Nullable ObjectFactory<AuditingHandler> handler;
@PrePersist
public void touchForCreate(Object target) {
Assert.notNull(target, "Entity must not be null");
if (handler != null) {
AuditingHandler object = handler.getObject();
if (object != null) {
object.markCreated(target);
}
}
}
@PreUpdate
public void touchForUpdate(Object target) {
Assert.notNull(target, "Entity must not be null");
if (handler != null) {
AuditingHandler object = handler.getObject();
if (object != null) {
object.markModified(target);
}
}
}
}
touchForCreate을 보면 @PrePersist안에서 target과 handler에 대해 null검사를 하면서 실제 날짜 데이터를 넣어주는 것으로 추정되는 markCraeted() 를 호출합니다.
target과 handler에 어떤 값이 들어오는지 궁금해서 디버깅 해봤습니다.
AuditingEntityListener있는 상황
@EnableJpaAuditing
@DataJpaTest
class CafePolicyRepositoryTest {
@Autowired
private CafePolicyRepository cafePolicyRepository;
@Autowired
private CafeRepository cafeRepository;
@Autowired
private OwnerRepository ownerRepository;
@Test
void auditingTest() {
ownerRepository.save(new Owner("name", "id", "pw", "phone"));
}
}
target에는 저장하려는 엔티티, handler에는 targetBeanName이 jpaAuditingHandler 로 들어오는 것을 확인할 수 있었습니다.
AuditingEntityListener없는 상황
// @EnableJpaAuditing
@DataJpaTest
class CafePolicyRepositoryTest {
@Autowired
private CafePolicyRepository cafePolicyRepository;
@Autowired
private CafeRepository cafeRepository;
@Autowired
private OwnerRepository ownerRepository;
@Test
void auditingTest() {
ownerRepository.save(new Owner("name", "id", "pw", "phone"));
}
}
target에는 마찬가지로 저장하려는 엔티티, handler에는 null 이 들어오면서 날짜가 생성되지 않고 넘어가는 것을 확인할 수 있었습니다.
'JPA' 카테고리의 다른 글
[JPA] 엔티티 구조 변경을 통한 로직 복잡성 낮추기, 쿼리 개선기 (2) | 2023.11.11 |
---|---|
[JPA] 상속관계 사용 시 주의점 (1) | 2023.10.05 |
[JPA] 엔티티 delete 시 발생하는 N+1 개선 (6) | 2023.10.04 |
[JPA] service 테스트 코드 개선하기 (3) | 2023.08.13 |
[JPA] 다양한 기본 키 자동 생성 전략 (6) | 2023.07.02 |