当前位置: 首页 > 知识库问答 >
问题:

模拟void方法返回空指针异常

陈铭晨
2023-03-14

我在尝试单元测试函数调用时遇到了一个问题。尽管调用已被存根,但由于无效方法调用MessageProducer.sendMessage()而失败。

请在下面找到我的代码的简化快照。我正在使用do答案()存根来模拟空方法(基于StackOverflow上的早期答案)。

我甚至尝试了其他选项的doThrow()doNot()存根,但当调用存根方法时,它们也会在相同的NPE中失败:(。

如果有人能提出解决方案/解决方法,我将不胜感激。非常感谢。

考试班

// Test class
@RunWith(MockitoJUnitRunner.class)
public class RetriggerRequestTest {
    @Mock
    private MessageProducer messageProducer;
 
    @InjectMocks
    private MigrationRequestServiceImpl migrationRequestService;
 
    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void sendRetriggerRequest() throws Exception {
        // Below two stubbings also not Work, NPE encountered!
        //doNothing().when(messageProducer).sendMessage(any(), anyLong());
        //doThrow(new Exception()).doNothing().when(messageProducer).sendMessage(any(), anyLong());

        doAnswer(new Answer<Void>() {
            public Void answer(InvocationOnMock invocation) {
                Object[] args = invocation.getArguments();
                System.out.println("called with arguments: " + Arrays.toString(args));
                return null;
            }
        }).when(messageProducer).sendMessage(any(EMSEvent.class), anyLong());

        try {
            // Gets Null pointer exception
            migrationRequestService.retriggerRequest(emsRetriggerRequest);
        }
        catch (Exception ex) {
            fail(ex.getMessage());
        }
    }

正在测试的实现类,来自该类的存根方法调用抛出NPE,如代码注释所示

@Service
@Transactional
public class MigrationRequestServiceImpl implements MigrationRequestService {
    @Autowired
    MessageProducer messageProducer;

    @Override
    public void retriggerRequest(EMSRetriggerRequestData emsRetriggerRequestData) throws EMSException {
        // Does a bunch of things
        submitTaskScheduledEventsToQueue(taskList);
    }

    private void submitTaskScheduledEventsToQueue(List<Task> taskList) {
        System.out.println("Debugging 1...");
        taskList.stream().forEach(task -> {
            System.out.println("Debugging 2...");
            Map<String, Object> detailsMap = new HashMap<String, Object>();
            EMSEvent event = new EMSEvent(EMSEventType.TASK_SCHEDULED);
            event.setDetails(detailsMap);

            LOGGER.info(ContextRetriever.getServiceContext(), ContextRetriever.getRequestContext(), "*** Re-submitting Task: *** " + task.getId());

            // ****Gives a null pointer exception here****
            messageProducer.sendMessage(event, eventsConfigProperties.getScheduledEventDelay());
        });
        System.out.println("Debugging 3...");
    }
}

自动连接类,该类被注入测试类,其方法是抛出NPE

@Service
public class MessageProducer {
private static final Logger logger = LoggerFactory.getLogger(MessageProducer.class);

        private final RabbitTemplate rabbitTemplate;

        @Autowired
        public MessageProducer(RabbitTemplate rabbitTemplate) {
                this.rabbitTemplate = rabbitTemplate;
    }   

    public void sendMessage(EMSEvent emsEvent, Long delay) {
        // code to send message to RabbitMQ here
    }   
}

共有2个答案

谭琛
2023-03-14

谢谢你Maciej的回答。实际上我不想对参数做任何事情,我只需要跳过这个方法调用。我只是用了一些假代码的do答案,因为doNo()或doThrow()不适用于这个方法。

不过,我还是解决了这个问题。正在注入模拟(MigrationRequestServiceImpl)的类的一个自动连接组件(EventsConfigProperty)在测试类中未被模拟!感谢@daniu指出这一点。

Mockito的堆栈跟踪对调试这个问题没有太大帮助,它只是在方法调用上出现了一个空指针异常,这让我觉得可能还有其他问题!

为这个错误道歉,我的错,但是谢谢你,很高兴了解ArgumentCaptor,将来可能需要它进行测试!

必须添加自动连接到MigrationRequestService类的条目。

// Test class
@RunWith(MockitoJUnitRunner.class)
public class RetriggerRequestTest {
    @Autowired
    EventsConfigProperties eventsConfigProperties;

    // Other declarations
}
丁淇
2023-03-14

如果只是想捕获参数并以某种方式处理/验证它们,请不要使用doAnswer。Mockito有一个定义的特性,名为ArgumentCaptor,就是专门为此设计的。通过使用它,你将不需要像现在这样讨价还价:

@Mock private MessageProducer messageProducer;

@Captor private ArgumentCaptor<Event> eventCaptor;
@Captor private ArgumentCaptor<Long> longCaptor;

@InjectMocks
private MigrationRequestServiceImpl migrationRequestService;

@Test
public void sendRetriggerRequest() throws Exception {
   // When
   migrationRequestService.retriggerRequest(emsRetriggerRequest);

   // Then
   verify(messageProducer).sendMessage(eventCaptor.capture(), longCaptor.capture());

   Event e = eventCaptor().getValue();
   Long l = longCaptor().getValue();
}
 类似资料:
  • 函数setUpMapIfNeeded()中的getFragmentManager返回。我把我的片段与activity_main.xml分开,这里是我的代码: activity_main.xml: fragment_main.xml: 这是我的*.Java文件: 我已尝试将getSupportFramentManager与Android.support.v4.app.DialogFragment支持

  • web.xml 应用程序-servlet.xml context.xml(在tomcat conf文件夹中) 谢谢你的建议和帮助。

  • 问题内容: 当我尝试在片段上显示地图时,它返回空指针异常。我已将所有权限添加到清单中。我将片段文件附加为其XML文件和log cat Chatffragment.java 其XML LOGCAT 第55和70行是googleMap = mapFrag.getMap(); initilizeMap(); 问题答案: 您必须等到地图加载完毕…。 编辑 检查编辑代码,使用ChildFragmentMan

  • 问题内容: 我正在为我的应用程序编写单元测试,我想知道Mockito框架是否有可能影响传递到返回无效类的方法中的对象。例如,调用一个包含方法的模拟验证类,该方法返回void,但通过作为参数传入的对象来跟踪各种更改和元数据。。 我为其他测试模拟了验证器类,但是对于这个测试,我需要模拟对不知道怎么做的cartItemsFilter对象的更改。 问题答案: 答案是肯定的,您可以,并且根据测试的需要,基本

  • 我有下面的课,我试图测试。我遇到问题的方法是,因为我试图存根/模拟行为,然后在测试中验证行为。

  • 我在玩C语言中的函数指针只是为了学习。我尝试调用一个空函数,并将其结果设置为int。 我从中得到的结果是: 在对它进行了一点研究之后,我意识到main正在函数()中打印printf语句中的字符数。为什么会这样?这是预期产出吗?