Spring Boot 如何修复单元测试中未保存的 transient 示例

bzzcjhmw  于 2023-03-02  发布在  Spring
关注(0)|答案(1)|浏览(125)

我想写一些测试,通过一些给定的属性找到一个对象;但在此之前,我想使用setUp方法初始化我想测试的对象。但此对象还依赖于另一个我想创建为模拟的对象,但我无法调用test methid方法,因为它会抛出此异常:数据访问权限无效应用程序使用异常:组织。休眠。瞬时属性值异常:对象引用了一个未保存的临时示例-刷新前保存临时示例。这是下面的示例代码,请告诉我缺少了什么以及如何使它更好,谢谢。

@DataJpaTest(properties = {
                "spring.jpa.properties.javax.persistence.validation.mode=none"
        })
@ExtendWith(MockitoExtension.class)
public class EmailVerificationTokenRepositoryTest {

    @Autowired
    private EmailVerificationTokenRepository repository;

    @Mock
    private UserRepository userRepository;

    /**
     * Test of findByToken method, of class EmailVerificationTokenRepository.
     */
    @BeforeEach
    public void setUp() {
        Set role = new HashSet<>();
        role.add("ROLE_USER");
        Users expected = Users.builder()
                .id(null)
                .email("david@gmail.com")
                .password("1234")
                .isEnabled(true)
                .isNonLocked(true)
                .roles(role)
                .createdAt(LocalDateTime.now())
                .build();               
        Mockito.when(userRepository.save(expected)).thenReturn(expected);
        Users actual = userRepository.save(expected);
        System.out.println(actual);
        EmailVerificationToken evt = EmailVerificationToken.builder()
                .createdAt(LocalDateTime.now())
                .expiresAt(LocalDateTime.now().plusHours(4))
                .token("token")
                .user(actual)
                .build();
        repository.save(evt);
    }

    @AfterEach
    public void tearDown() {
        repository.deleteAll();
    }

    @Test
    @DisplayName("Test to find Email Verification Object By Token")
    public void testFindByToken() {
        String token = "token";
        Optional<EmailVerificationToken> result = repository.findByToken(token);
        assertTrue(result.isPresent());
    }

    /**
     * Test of findByUser method, of class EmailVerificationTokenRepository.
     */
    @Test
    @Disabled
    @DisplayName("Test to find Email Verification Object By User")
    public void testFindByUser() {
        //Given
        Set role = new HashSet<>();
        role.add("ROLE_USER");
        Users user = Users.builder()
                .id(null)
                .email("david@gmail.com")
                .password("1234")
                .isEnabled(true)
                .isNonLocked(true)
                .roles(role)
                .createdAt(LocalDateTime.now())
                .build();
        Optional<EmailVerificationToken> result = repository.findByUser(user);
        assertTrue(result.isPresent());
    }

}

这些是我的日志:

2023-02-26 18:57:11.507  INFO [,,] 12780 --- [           main] u.r.EmailVerificationTokenRepositoryTest : Started EmailVerificationTokenRepositoryTest in 7.485 seconds (JVM running for 11.929)
2023-02-26 18:57:11.588  INFO [,,] 12780 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@2b76ff4e testClass = EmailVerificationTokenRepositoryTest, testInstance = com.slinkdigital.user.repository.EmailVerificationTokenRepositoryTest@73eae5f, testMethod = testFindByToken@EmailVerificationTokenRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@7a1234bf testClass = EmailVerificationTokenRepositoryTest, locations = '{}', classes = '{class com.slinkdigital.user.UserApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{spring.jpa.properties.javax.persistence.validation.mode=none, org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@2f62ea70 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6f3187b0, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@189cbd7c, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@723e88f9, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@6ea2bc93, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@474abc3a, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@48c76607, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false, 'org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.mocks' -> org.mockito.internal.configuration.InjectingAnnotationEngine$$Lambda$406/0x0000000800e49720@4902c584]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@42ef5216]; rollback [true]
Users(id=null, email=david@gmail.com, password=1234, createdAt=2023-02-26T18:57:11.641991300, isNonLocked=false, isEnabled=false, roles=[ROLE_USER])
2023-02-26 18:57:11.774 DEBUG [,,] 12780 --- [           main] org.hibernate.SQL                        : 
    insert 
    into
        email_verification_token
        (id, created_at, expires_at, token, user_id) 
    values
        (default, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        email_verification_token
        (id, created_at, expires_at, token, user_id) 
    values
        (default, ?, ?, ?, ?)
2023-02-26 18:57:12.033  INFO [,,] 12780 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@2b76ff4e testClass = EmailVerificationTokenRepositoryTest, testInstance = com.slinkdigital.user.repository.EmailVerificationTokenRepositoryTest@73eae5f, testMethod = testFindByToken@EmailVerificationTokenRepositoryTest, testException = org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.slinkdigital.user.domain.EmailVerificationToken.user -> com.slinkdigital.user.domain.Users; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.slinkdigital.user.domain.EmailVerificationToken.user -> com.slinkdigital.user.domain.Users, mergedContextConfiguration = [MergedContextConfiguration@7a1234bf testClass = EmailVerificationTokenRepositoryTest, locations = '{}', classes = '{class com.slinkdigital.user.UserApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{spring.jpa.properties.javax.persistence.validation.mode=none, org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@2f62ea70 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6f3187b0, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@189cbd7c, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@723e88f9, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@6ea2bc93, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@474abc3a, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@48c76607, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false, 'org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.mocks' -> org.mockito.internal.configuration.InjectingAnnotationEngine$$Lambda$406/0x0000000800e49720@4902c584]]
Tests run: 2, Failures: 0, Errors: 1, Skipped: 1, Time elapsed: 10.605 s <<< FAILURE! - in com.slinkdigital.user.repository.EmailVerificationTokenRepositoryTest
testFindByToken  Time elapsed: 0.024 s  <<< ERROR!
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.slinkdigital.user.domain.EmailVerificationToken.user -> com.slinkdigital.user.domain.Users; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.slinkdigital.user.domain.EmailVerificationToken.user -> com.slinkdigital.user.domain.Users
    at com.slinkdigital.user.repository.EmailVerificationTokenRepositoryTest.testFindByToken(EmailVerificationTokenRepositoryTest.java:76)
Caused by: java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.slinkdigital.user.domain.EmailVerificationToken.user -> com.slinkdigital.user.domain.Users
    at com.slinkdigital.user.repository.EmailVerificationTokenRepositoryTest.testFindByToken(EmailVerificationTokenRepositoryTest.java:76)
Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.slinkdigital.user.domain.EmailVerificationToken.user -> com.slinkdigital.user.domain.Users
    at com.slinkdigital.user.repository.EmailVerificationTokenRepositoryTest.testFindByToken(EmailVerificationTokenRepositoryTest.java:76)

我将感激每一个帮助我可以得到,谢谢。

3mpgtkmj

3mpgtkmj1#

导致原因:org. hib.临时属性值异常:对象引用未保存的 transient 示例-刷新前保存 transient 示例:* * com. slink数字.用户.域.电子邮件验证令牌.用户-〉com. slink数字.用户.域.用户
错误消息已经给了你一些提示。这是因为你创建的EmailVerificationToken的用户不是由JPA管理的。因为你使用了一个mocked的UserRepository来创建它。所以从JPA的Angular 来看,这个用户示例从来就不存在。
您必须使用
实际**UserRepository创建用户或获取现有用户,而不是使用模拟。没有其他选择。为什么要使用模拟UserRepository而不是实际UserRepository
因此,更改为以下内容应该可以解决该问题:

@Autowired
private UserRepository userRepository;

相关问题