如何为单元测试伪造上次修改的时间?

9wbgstp7  于 2021-07-07  发布在  Java
关注(0)|答案(2)|浏览(466)

我有一个简单的实体,它要求在persist上更新上次修改的时间。

@Data      // Lombok thing
@Entity
@Table(name = "MY_ENTITY")
public class MyEntity {

    @Column(name = "LAST_MODIFIED", nullable = false)
    private LocalDateTime lastModified;

    // irrelevant columns including id omitted

    @PrePersist
    public void initializeUUID() {
        lastModified = LocalDateTime.now();
    }
}

我需要实现一个作业,该作业查询超过某个时间(比如说一天)的实体,修改其状态并持久化它们。我在为单元测试创建数据时遇到了一个问题,这个单元测试涵盖了这样的用例。
虽然我手动设置 lastModified 时间,时间 @PrePersist 使其更改,而不考虑设置值。

@Autowired  // Spring Boot tests are configured against in-memory H2 database
MyEntityRepository myEntityRepository;
var entity = new MyEntity();
entity.setLastModified(LocalDateTime.now().minusDays(3));
myEntityRepository.entity(entity);

问题:如何准备预持久化数据( lastModified )而不是彻底改变 MyEntity 只是为了单元测试?欢迎使用mockito的解决方案。
注意我使用springboot+junit5+mockito
我尝试过的事情:
如何用mockito和junit来模拟持久化和实体:模拟持久化实体不是一个好办法,因为我需要将实体持久化到h2中进行进一步检查。此外,我还尝试使用spy bean使用这个技巧spring boot#7033,得到了相同的结果。
hibernate提示:如何为所有实体激活实体侦听器:使用配置的静态嵌套类以编程方式添加侦听器 @TestConfiguration 对于单元测试范围。这东西根本不叫。

@TestConfiguration
public static class UnitTestConfiguration {                 // logged as  registered

    @Component
    public static class MyEntityListener implements PreInsertEventListener {

        @Override
        public boolean onPreInsert(PreInsertEvent event) {  // not called at all
            Object entity = event.getEntity();
            log.info("HERE {}" + entity);                   // no log appears
            // intention to modify the `lastModified` value
            return true;
        }
    }

脏方法:创建方法级类扩展 MyEntity@PrePersist 它“覆盖”了 lastModified 价值观。结果是 org.springframework.dao.InvalidDataAccessApiUsageException . 为了解决这个问题,这个实体依赖于 @Inheritance 注解(jpa:entity-extend-with-entity),我不想仅仅为了单元测试而使用它。不能在生产代码中扩展实体。

qkf9rpyu

qkf9rpyu1#

有了Mockito你也许可以做这样的事?

MyEntity sut = Mockito.spy(new MyEntity());

Mockito.doNothing().when(sut).initializeUUID();

但我不确定它到底适合你的测试。
另外两个选择
嘲弄 LocalDateTime.now() 让它返回你想要的值。但是,persist进程中的其他代码可能会调用此方法,并且可能不喜欢它。如果是这样的话,就转到另一个选项
包裹 LocalDateTime.now() 在您自己的类中使用一个静态方法并模拟它。遗憾的是,它只涉及对实体类的微小更改,但只涉及对 LocalDateTime.now() 会被嘲笑的。
在这个例子中,我没有描述如何使用mockito进行模拟,因为我不熟悉它。我只用过jmockit。但以上就是原则。

fdbelqdn

fdbelqdn2#

您可以使用spring数据jpa auditingentitylistener。
只需通过 @org.springframework.data.jpa.repository.config.EnableJpaAuditing 并提供自定义 dateTimeProviderRef 这样地:

@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "myAuditingDateTimeProvider")
public class JpaAuditingConfig {

    @Bean(name = "myAuditingDateTimeProvider")
    public DateTimeProvider dateTimeProvider(Clock clock) {
        return () -> Optional.of(now(clock));
    }
}

你的实体可以是这样的:

@Data      // Lombok thing
@Entity
@Table(name = "MY_ENTITY")
@EntityListeners(AuditingEntityListener.class)
public class MyEntity {

    @LastModifiedDate
    private LocalDateTime lastModified;

    // irrelevant columns including id omitted
}

在上面的例子中 java.time.Clock 可以通过spring提供,它已经可以解决您关于测试的问题。但是您也可以提供一个专用的测试配置来指定不同的/mock DateTimeProvider .
请注意,这里提到的解决方案不是纯单元测试方法。但是基于你的问题和你尝试过的事情,我得出结论,使用spring的解决方案是可行的。

相关问题