cheese10yun / blog-comment

0 stars 0 forks source link

JPA 기반 테스트 코드 작성 팁 - Yun Blog | 기술 블로그 #44

Open utterances-bot opened 3 years ago

utterances-bot commented 3 years ago

JPA 기반 테스트 코드 작성 팁 - Yun Blog | 기술 블로그

Yun Blog | 기술 블로그

https://cheese10yun.github.io/jpa-test-support/

hkleev2 commented 3 years ago

안녕하세요 좋은 글 감사합니다

그런데 트랜잭션 커밋 위치를 바꿔야 되지 않나용..?

protected <T> List<T> saveAll(List<T> entities) {
    transaction.begin();

    try {
        for (var entity : entities) {
            entityManager.persist(entity);
            setupEntities.add(entity);
        }
        entityManager.flush();
        transaction.commit();
        entityManager.clear();
    } catch (Exception e) {
        transaction.rollback();
        throw e;
    }

    return entities;
}

그리고 테스트용 데이터 삭제안되는거 아래처럼 처리하고 있는데 혹시 문제될 게 있는지 궁금합니다!

private final List<Object> setupEntities = new ArrayList<>();

@AfterEach
protected void deleteSetups() {
    transaction.begin();

    try {
        for (var entity: setupEntities) {
            entityManager.remove(entityManager.merge(entity));
            entityManager.flush();
        }
        transaction.commit();
        entityManager.clear();
        setupEntities.clear();
    } catch (Exception e) {
        transaction.rollback();
        throw e;
    }
}
cheese10yun commented 3 years ago

@hkleev2

그런데 트랜잭션 커밋 위치를 바꿔야 되지 않나용..?

entityManager.persist(entity)
entityManager.flush()
transaction.commit()
entityManager.clear()

트랜잭션 commit시에 flush를 동작시키기 때문에 사실 굳이 flush를 동작 시키지 않아도 되긴 합니다. 그럼에도 flush를 명시적으로 동작시키는건 아무래도 테스트 코드다 보니 앞에 어떤 작업들이 있을지 몰라서 flush를 명시적으로 동작 시켰습니다.

그리고 테스트용 데이터 삭제안되는거 아래처럼 처리하고 있는데 혹시 문제될 게 있는지 궁금합니다!

이렇게 선언하는 것보다 주어진 테스트 코드의 클래스에 @Transactional 추가하고 아래처럼 사용하면 자동으로 rollback이 진행됩니다.

@SpringBootTest(properties = ["spring.profiles.active=test"])
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class SpringTestSupport {

    @Autowired
    private lateinit var entityManager: EntityManager

    protected fun <T> save(entity: T): T {
        entityManager.persist(entity)
        flushAndClearPersistentContext()
        return entity
    }

    private fun flushAndClearPersistentContext() {
        entityManager.flush()
        entityManager.clear()
    }
}

@Transactional
internal class BookFindServiceTest(
    private val bookRepository: BookRepository,
    private val bookFindService: BookFindService
) : SpringTestSupport() {

    @Test
    internal fun `findById 존재하는 경우`() {
        //given
        val book = bookRepository.save(
            Book(
                title = "title",
                writer = "writer",
                publisher = "publisher",
                price = BigDecimal.TEN
            )
        )
       // 위 save 자동 롤백
    }
}

이렇게 사용하는 것이 더 효율적이라고 생각합니다.

해당 포스팅은 Spring Batch 테스트 코드에서 @Transactional을 물고 시작을 할 수 없어 엔티티 메니저를 직접 생성해서 테스트를 진행하는 경우에 해당 하는 자료입니다. Spring Batch 테스트 코드가 아니라면 위 같은 형식으로 하는것을 개인적으로 권장드립니다.