使用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+版本)来拦截,让它返回你预先定义好的auditReaderMock对象:- 在测试类上添加
@ExtendWith(MockitoExtension.class),替代手动调用MockitoAnnotations.openMocks(this) - 在测试方法中通过
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
相关产品推荐
相关产品推荐

