You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Hibernate Envers的JUnit测试中EntityManager为空问题排查

问题

我正在测试一个采用Hibernate Envers实现回滚功能的服务,业务逻辑运行正常,但测试环节出现问题。服务类ObjectRollbackServiceImpl执行AuditReader auditReader = AuditReaderFactory.get(entityManager);时,EntityManager为空。

服务代码片段

@Slf4j
@AllArgsConstructor
@Service
public class ObjectRollbackServiceImpl implements ObjectRollbackService {

  @Autowired
  private EntityManager entityManager;

  @Autowired private ObjectRollbackRepository objectRollbackRepository;

  private void restorePreviousVersionOfObject(Long id, Boolean isDelete) {

    AuditReader auditReader = AuditReaderFactory.get(entityManager);

    List<Number> revNumbers = auditReader.getRevisions(ObjectEntity.class, id); 

JUnit测试代码

class ObjectRollbackServiceTests {

  private static final Long objectId = 1L;
  @InjectMocks
  ObjectRollbackServiceImpl objectRollbackServiceImpl;
  @Mock
  ObjectRollbackRepository objectRollbackRepository;

  @PersistenceUnit
  private EntityManagerFactory entityManagerFactory;


  @Mock
  AuditReader auditReader;


  @BeforeEach
  void setUp() throws Exception {
    MockitoAnnotations.openMocks(this);

  }

  @Test
  void testRestorePreviousVersionOfEditedObjectOk() {
    ObjectEntity object = ObjectEntity.builder().build();
    List<Number> listRevisions = Lists.newArrayList();
    listRevisions.add(1);

    when(auditReader.getRevisions(any(), any(Long.class))).thenReturn(listRevisions);
    when(auditReader.find(any(), any(Long.class), any(Number.class))).thenReturn(object);
    when(objectRollbackRepository.findById(any(Long.class))).thenReturn(Optional.of(object));
    when(objectRollbackRepository.saveAndFlush(any(ObjectEntity.class))).thenReturn(object);
    doNothing().when(objectRollbackRepository).updateAuditDates(any(ZonedDateTime.class)
    , any(Integer.class)
    , any(AuditorBO.class)
    , any(Long.class));

    doNothing().when(objectRollbackRepository).deleteRevisionsNotEqualsToCurrentAndPrevious(any(Integer.class), any(Long.class));

    assertDoesNotThrow(() -> objectRollbackServiceImpl.restorePreviousVersionOfEditedObject(1L));


  }


}
解决方案

你遗漏了对EntityManager的模拟注入,以及对AuditReaderFactory静态方法的拦截处理,导致测试时entityManager为空。具体修正步骤如下:

  • 模拟EntityManager并注入
    在测试类中添加@Mock EntityManager entityManager;,这样@InjectMocks会自动将其注入到ObjectRollbackServiceImpl中,避免空指针问题。

  • 拦截AuditReaderFactory的静态调用
    AuditReaderFactory.get()是静态方法,需要用Mockito的静态Mock功能(需Mockito 3.4+版本)来拦截,让它返回你预先定义好的auditReader Mock对象:

    1. 在测试类上添加@ExtendWith(MockitoExtension.class),替代手动调用MockitoAnnotations.openMocks(this)
    2. 在测试方法中通过MockedStatic包裹静态Mock逻辑
  • 修正后的测试类关键代码

@ExtendWith(MockitoExtension.class)
class ObjectRollbackServiceTests {

  private static final Long objectId = 1L;
  @InjectMocks
  ObjectRollbackServiceImpl objectRollbackServiceImpl;
  @Mock
  ObjectRollbackRepository objectRollbackRepository;
  @Mock // 新增EntityManager的Mock实例
  private EntityManager entityManager;
  @Mock
  AuditReader auditReader;

  @Test
  void testRestorePreviousVersionOfEditedObjectOk() {
    ObjectEntity object = ObjectEntity.builder().build();
    List<Number> listRevisions = Lists.newArrayList();
    listRevisions.add(1);

    // 静态Mock AuditReaderFactory,指定返回预设的auditReader
    try (MockedStatic<AuditReaderFactory> mockedAuditReaderFactory = Mockito.mockStatic(AuditReaderFactory.class)) {
      mockedAuditReaderFactory.when(() -> AuditReaderFactory.get(entityManager)).thenReturn(auditReader);

      when(auditReader.getRevisions(any(), any(Long.class))).thenReturn(listRevisions);
      when(auditReader.find(any(), any(Long.class), any(Number.class))).thenReturn(object);
      when(objectRollbackRepository.findById(any(Long.class))).thenReturn(Optional.of(object));
      when(objectRollbackRepository.saveAndFlush(any(ObjectEntity.class))).thenReturn(object);
      doNothing().when(objectRollbackRepository).updateAuditDates(any(ZonedDateTime.class), any(Integer.class), any(AuditorBO.class), any(Long.class));
      doNothing().when(objectRollbackRepository).deleteRevisionsNotEqualsToCurrentAndPrevious(any(Integer.class), any(Long.class));

      assertDoesNotThrow(() -> objectRollbackServiceImpl.restorePreviousVersionOfEditedObject(1L));
    }
  }
}

另外,你的服务类使用了@AllArgsConstructor,其实可以省略@Autowired注解,但这不是导致测试失败的原因。

内容的提问来源于stack exchange,提问作者Rachel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 14:50:32