fixtureMonkey의 entity 생성 문제

rejs·2026년 1월 24일
org.springframework.dao.DataIntegrityViolationException: could not execute statement [(conn=5) Data too long for column 'name' at row 1] [insert into members (created_at,deleted_at,email,name,provider,role,updated_at) values (?,?,?,?,?,?,?)]; SQL [insert into members (created_at,deleted_at,email,name,provider,role,updated_at) values (?,?,?,?,?,?,?)]
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:297)
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:256)
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:241)
	at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:560)
	at app//org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
	at app//org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:343)
	at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:160)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:165)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
	at app/jdk.proxy3/jdk.proxy3.$Proxy201.save(Unknown Source)
	at app//com.rejs.flashnote.domain.note.service.NoteGroupServiceIntegrationTest.deleteNoteGroup_Integration(NoteGroupServiceIntegrationTest.java:99)
	at java.base@21.0.4/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base@21.0.4/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base@21.0.4/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.hibernate.exception.DataException: could not execute statement [(conn=5) Data too long for column 'name' at row 1] [insert into members (created_at,deleted_at,email,name,provider,role,updated_at) values (?,?,?,?,?,?,?)]

https://naver.github.io/fixture-monkey/v1-0-0-kor/docs/introduction/overview/
테스트를 위한 데이터 생성에서 fixtureMoney를 사용한다.

에러 메시지를 보면 그 원인을 일목요연하게 파악할 수 있다.

entity의 ddl보다 긴 범위의 랜덤 데이터가 들어오게 된 것이다.

덱 삭제 통합 테스트: DB에서 해당 데이터가 완전히 제거되어야 한다
org.springframework.dao.DataIntegrityViolationException: could not execute statement [(conn=5) Duplicate entry 'z-google' for key 'uk_members_email_provider'] [insert into members (created_at,deleted_at,email,name,provider,role,updated_at) values (?,?,?,?,?,?,?)]; SQL [insert into members (created_at,deleted_at,email,name,provider,role,updated_at) values (?,?,?,?,?,?,?)]; constraint [uk_members_email_provider]
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:294)
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:256)
	at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:241)
	at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:560)
	at app//org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
	at app//org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:343)
	at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:160)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:165)
	at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
	at app/jdk.proxy3/jdk.proxy3.$Proxy208.save(Unknown Source)
	at app//com.rejs.flashnote.domain.cards.service.DeckServiceTest.setup(DeckServiceTest.java:49)
	at java.base@21.0.4/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base@21.0.4/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base@21.0.4/java.util.ArrayList.forEach(ArrayList.java:1596)

이번엔 가끔씩 나오는 문제인데 email unique가 문제가 생긴것을 볼 수 있다.

'z-google'이라니 email 랜덤값이z인거 같은데

최소길이를 정해줘야한다

public class TestDataBuilderGroup implements ArbitraryBuilderGroup {

    @Override
    public ArbitraryBuilderCandidateList generateCandidateList() {
        return ArbitraryBuilderCandidateList.create()
                .add(
                        ArbitraryBuilderCandidateFactory.of(Member.class)
                                .builder(
                                        builder->builder
                                                .setNull(javaGetter(Member::getId))
                                                .set(javaGetter(Member::getName), Arbitraries.strings().ofMaxLength(50))
                                                .set(javaGetter(Member::getEmail), Arbitraries.strings().alpha().ofMinLength(10).ofMaxLength(250))
                                                .set(javaGetter(Member::getRole), MemberRole.ROLE_USER)
                                                .set(javaGetter(Member::getProvider), "google")
                                                .setNull(javaGetter(Member::getDeletedAt))
                                )
                )
                .add(
                        ArbitraryBuilderCandidateFactory.of(Note.class)
                                .builder(
                                        builder->builder
                                                .setNull(javaGetter(Note::getId))
                                                .set(javaGetter(Note::getTitle), Arbitraries.strings().ofMaxLength(50))
                                                .setNull(javaGetter(Note::getDeletedAt))
                                )
                )
                .add(
                        ArbitraryBuilderCandidateFactory.of(Deck.class)
                                .builder(
                                        builder->builder
                                                .setNull(javaGetter(Deck::getId))
                                                .set(javaGetter(Deck::getName), Arbitraries.strings().ofMaxLength(50))
                                                .set(javaGetter(Deck::getOriginalType), DeckOriginalType.ORIGINAL)
                                                .set(javaGetter(Deck::getCardCounts), 0)
                                                .setNull(javaGetter(Deck::getDeletedAt))
                                )
                )
                .add(
                        ArbitraryBuilderCandidateFactory.of(Card.class)
                                .builder(
                                        builder->builder
                                                .setNull(javaGetter(Card::getId))
                                                .set(javaGetter(Card::getFront), Arbitraries.strings().ofMaxLength(50))
                                                .set(javaGetter(Card::getBack), Arbitraries.strings().ofMaxLength(50))
                                                .set(javaGetter(Card::getState), State.LEARNING)
                                                .set(javaGetter(Card::getDue), Instant.now())
                                                .set(javaGetter(Card::getLastReviewAt), Instant.now())
                                                .set(javaGetter(Card::getFsrsData), "{}")
                                                .setNull(javaGetter(Card::getDeletedAt))
                                )
                )

                ;
    }

    public static FixtureMonkey fixtureMonkey(){
        return FixtureMonkey.builder()
                .objectIntrospector(BuilderArbitraryIntrospector.INSTANCE)
                .plugin(new JakartaValidationPlugin())
                .registerGroup(new TestDataBuilderGroup())
                .build();
    }
}

중요 entity마다 입력값을 미리 정하는 규칙을 정하고 테스트마다 재사용하도록 하면 문제를 해결할 수 있다.

0개의 댓글