繁体   English   中英

如何伪造单元测试的最后修改时间?

[英]How to fake last modified time for unit tests?

我有一个简单的实体,要求上次修改时间应该持续更新。

@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();
    }
}

我需要实现一个作业,该作业查询早于特定时间(比方说一天)的此类实体,修改其 state 并保留它们。 我在为涵盖此类用例的单元测试创建数据时遇到问题。

尽管我手动设置了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);

问题:如何在不为了单元测试而大幅修改MyEntity class 的情况下准备预先保留的数据 ( lastModified )? 欢迎使用Mockito的解决方案。

注意我使用 Spring Boot + jUnit 5 + Mockito

我尝试过的事情:

  • 如何使用 Mockito 和 jUnit 模拟持久化和实体:Mocking 持久化实体不是 go 的一种方式,因为我需要将实体持久化在 H2 中以进行进一步检查。 此外,我尝试使用此技巧Spring Boot #7033来使用间谍 bean ,结果相同。

  • Hibernate 提示:如何为所有实体激活实体侦听器:使用 static 嵌套@TestConfiguration为单元测试 scope 配置的 @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; } }
  • 肮脏的方式:创建一个方法级 class 扩展MyEntity@PrePersist “覆盖” lastModified值。 它导致org.springframework.dao.InvalidDataAccessApiUsageException 要修复它,此类实体依赖于@Inheritance注释( JPA: Entity extend with entity ),我不想仅仅为了单元测试而使用它。 不得在生产代码中扩展该实体。

您可以使用 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提供 java.time.Clock,这已经可以解决您关于测试的问题。 但是您也可以提供一个专用的测试配置,指定一个不同的/模拟的DateTimeProvider

请注意,这里提到的解决方案不是纯单元测试方法。 但是根据您的问题和您尝试过的事情,我得出结论,使用 Spring 的解决方案是可行的。

使用 Mockito 你也许可以做这样的事情?

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

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

但我不确定它在您的测试中的确切位置。


其他两个选项

  1. 模拟LocalDateTime.now()并让它返回您想要的值。 但也许持久化进程中的其他代码调用了这个方法,可能并不喜欢它。 如果是这样,请转到另一个选项
  2. 使用 static 方法将LocalDateTime.now()包装在您自己的 class 中,然后模拟它。 遗憾的是涉及对您的实体 class 的微小更改,但只会模拟对LocalDateTime.now()的调用。

在这种情况下,我没有描述如何使用 Mockito 进行模拟,因为我不熟悉它。 我只使用过 JMockit。 但以上就是原则。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM