从JMockit迁移至Mockito遇空指针与验证异常求助
问题排查与解决方案
你遇到的核心问题是Mockito的@Mock注解对象没有被正确初始化,导致timerSessionBean并不是一个真正的Mock实例,所以verify()方法抛出了"is not a mock"的异常。
为什么会出现这个问题?
Mockito的@Mock注解只是标记了需要被Mock的字段,但它不会自动完成Mock对象的创建——你需要触发Mockito的初始化逻辑,否则这些字段会保持默认的null(或者如果是类类型的话,是未被代理的原始对象),自然无法通过verify()的校验。
两种快速解决方法
方法1:使用Mockito扩展类(JUnit 5推荐)
在你的测试类上添加@ExtendWith(MockitoExtension.class)注解,它会自动帮你初始化所有标记了@Mock的字段:
import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class YourTestClass { // 替换成你的测试类名 @Mock public EntityManager entityManager; @Mock public TimerSessionBean timerSessionBean; @Mock public Client client; // ... 其他字段和测试代码 }
方法2:手动初始化Mock对象
如果不想用扩展类,可以在@BeforeEach方法的最开头调用MockitoAnnotations.openMocks(this),手动触发Mock对象的初始化:
@BeforeEach final void beforeEach() { // 先初始化所有@Mock标记的对象 MockitoAnnotations.openMocks(this); request = new Request(); message = new Message(); String spaceId = "SPACE_ID"; String threadId = "THREAD_ID"; caseSetReminder = new CaseSetReminder(); caseSetReminder.entityManager = entityManager; caseSetReminder.timerSessionBean = timerSessionBean; // ... 后续代码 }
额外注意点
你在@BeforeEach里直接给timerSessionBean.nextReminderDate赋值的操作需要留意:如果nextReminderDate是TimerSessionBean的私有字段,直接赋值会报错;如果是公共字段,虽然能运行,但Mock对象的字段修改通常建议用when()或者doReturn()来模拟,更符合Mockito的使用规范:
// 替换直接赋值的代码 when(timerSessionBean.nextReminderDate).thenReturn(reminder.getWhen());
修改后的测试类示例(方法1)
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class CaseSetReminderTest { @Mock public EntityManager entityManager; @Mock public TimerSessionBean timerSessionBean; @Mock public Client client; private CaseSetReminder caseSetReminder; private Request request; private Message message; @BeforeEach final void beforeEach() { request = new Request(); message = new Message(); String spaceId = "SPACE_ID"; String threadId = "THREAD_ID"; caseSetReminder = new CaseSetReminder(); caseSetReminder.entityManager = entityManager; caseSetReminder.timerSessionBean = timerSessionBean; ThreadM thread = new ThreadM(); thread.setName("spaces/" + spaceId + "/thread/" + threadId + ""); Sender sender = new Sender(); sender.setName("MyName"); message.setSender(sender); message.setThread(thread); Reminder reminder = new Reminder("Do Something", ZonedDateTime.now(ZoneId.of("Europe/Athens")).plusMinutes(10), "DisplayName", "Europe/Athens", spaceId, threadId); reminder.setReminderId(1); // 用Mockito的方式模拟字段值 when(timerSessionBean.nextReminderDate).thenReturn(reminder.getWhen()); } @Test void mockitoTest() throws Exception { final String expectedDate = "12/12/2019 12:00 athens"; message.setText("remind me ' set next reminder Test' at " + expectedDate); request.setMessage(message); caseSetReminder.setRequest(request); caseSetReminder.setReminder(); verify(timerSessionBean , times(1)).setNextReminder(any(Reminder.class), any(ZonedDateTime.class)); } }
内容的提问来源于stack exchange,提问作者Theodosis
相关产品推荐
相关产品推荐

