有没有办法在测试中选择性地禁用对实体修改的审计?
Posted
技术标签:
【中文标题】有没有办法在测试中选择性地禁用对实体修改的审计?【英文标题】:Is there a way to selectively disable auditing for entity modifications in tests? 【发布时间】:2020-01-21 09:43:23 【问题描述】:我有几个实体需要审核。审计是通过使用以下 JPA 事件侦听器实现的。
public class AuditListener
@PrePersist
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public void setCreatedOn(Auditable auditable)
User currentUser = getCurrentUser();
Long entityId = auditable.getId();
Audit audit;
if (isNull(entityId))
audit = getCreatedOnAudit(currentUser);
else
audit = getUpdatedOnAudit(auditable, currentUser);
auditable.setAudit(audit);
@PreUpdate
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public void setUpdatedOn(Auditable auditable)
User currentUser = getCurrentUser();
auditable.setAudit(getUpdatedOnAudit(auditable, currentUser));
private Audit getCreatedOnAudit(User currentUser)
return Audit.builder()
.userCreate(currentUser)
.dateCreate(now())
.build();
private Audit getUpdatedOnAudit(Auditable auditable, User currentUser)
AuditService auditService = BeanUtils.getBean(AuditService.class);
Audit audit = auditService.getAudit(auditable.getClass().getName(), auditable.getId());
audit.setUserUpdate(currentUser);
audit.setDateUpdate(now());
return audit;
private User getCurrentUser()
String userName = "admin";
UserService userService = BeanUtils.getBean(UserService.class);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (nonNull(auth))
Object principal = auth.getPrincipal();
if (principal instanceof UserDetails)
userName = ((UserDetails)principal).getUsername();
return userService.findByLogin(userName);
在测试环境(单元测试,e2e)中,我希望能够手动设置审核。
这可能吗?我之前曾尝试用 Spring AOP 解决这个问题,但不幸的是没有成功。我认为,Spring AOP 可以允许通过在切入点中使用各种组合来选择性地设置审计:
Audit for cascade saving by using Spring AOP Why aspect not triggered for owner side in OneToOne relationship?有没有办法通过使用 JPA 功能选择性地设置审计?
【问题讨论】:
简单地模拟/监视UserService
(使用@MockBean
或测试上下文的简单bean 定义覆盖)怎么样?您应该能够以类似的方式覆盖创建/修改时间,使用now(clock)
而不是now()
并注入Clock
,然后您可以使用模拟/固定瞬间覆盖其提供程序定义以进行测试。顺便说一句,你不需要BeanUtils.getBean(UserService.class)
,Spring 支持 JPA 侦听器中的依赖注入
【参考方案1】:
由Naros解决为was suggested。
pre-persist 和 pre-update 的逻辑被移到一个单独的可注入组件中,AuditListener
将执行委托给该组件的不同实现,具体取决于当前的活动配置文件。
对于 Spring Boot 2.1.0:
通用接口:
public interface AuditManager
void performPrePersistLogic(Auditable auditable);
void performPreUpdateLogic(Auditable auditable);
JPA 回调监听器:
@Component
@RequiredArgsConstructor
public class AuditListener
private final AuditManager auditManager;
@PrePersist
public void setCreatedOn(Auditable auditable)
auditManager.performPrePersistLogic(auditable);
@PreUpdate
public void setUpdatedOn(Auditable auditable)
auditManager.performPreUpdateLogic(auditable);
通用接口的实现,用于测试环境和本地环境:
@RequiredArgsConstructor
public class AuditChanger implements AuditManager
private final UserService userService;
private final AuditService auditService;
@Override
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public void performPrePersistLogic(Auditable auditable)
// logic here
@Override
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
public void performPreUpdateLogic(Auditable auditable)
// logic here
public class AuditNoChanger implements AuditManager
// mostly similar
...
允许 Spring 根据当前活动的配置文件执行不同实现注入的配置:
@Configuration
public class AuditConfig
@Bean
@Profile("e2e")
public AuditManager getAuditNoChanger()
return new AuditNoChanger();
@Bean
@Profile("local")
public AuditManager getAuditChanger(AuditService auditService,
CurrentUserService currentUserService)
return new AuditChanger(auditService, currentUserService);
还需要允许在 *.yml 文件中覆盖 bean:
spring:
main:
allow-bean-definition-overriding: true
【讨论】:
以上是关于有没有办法在测试中选择性地禁用对实体修改的审计?的主要内容,如果未能解决你的问题,请参考以下文章
如果在 Sonata Admin 中选择了单选按钮,如何禁用单选按钮
在不修改实体类(注释)或数据上下文(使用 fluentapi)的情况下禁用标识(自动递增)