[英]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();
但我不确定它在您的测试中的确切位置。
其他两个选项
LocalDateTime.now()
并让它返回您想要的值。 但也许持久化进程中的其他代码调用了这个方法,可能并不喜欢它。 如果是这样,请转到另一个选项LocalDateTime.now()
包装在您自己的 class 中,然后模拟它。 遗憾的是涉及对您的实体 class 的微小更改,但只会模拟对LocalDateTime.now()
的调用。在这种情况下,我没有描述如何使用 Mockito 进行模拟,因为我不熟悉它。 我只使用过 JMockit。 但以上就是原则。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.