简体   繁体   English

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

[英]Mocking void method returning a null pointer exception

I am facing an issue when trying to unit test a function call. 尝试对功能调用进行单元测试时,我遇到了一个问题。 The call is failing for a void method invocation messageProducer.sendMessage() even though it has been stubbed. 该调用因无效方法调用messageProducer.sendMessage()而失败。

Please find below a simplified snapshot of my code. 请在下面找到我的代码的简化快照。 I am using a doAnswer() stubbing to mock the void method (based on earlier answers on StackOverflow). 我正在使用doAnswer()桩来模拟void方法(基于先前关于StackOverflow的答案)。

I even tried the other options of doThrow() and doNothing() stubbings, but they also fail with the same NPE when calling the stubbed method :(. 我什至尝试了doThrow()doNothing()存根的其他选项,但是在调用存根方法:(时,它们也使用相同的NPE失败。

Appreciate if someone could suggest a solution/workaround. 欣赏是否有人可以提出解决方案/解决方法。 Many thanks. 非常感谢。

Test Class 测试班

// 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());
        }
    }

Implementation Class being tested, the stubbed method call from this class throws NPE as indicated in code comments 正在测试实现类,该类中的存根方法调用将抛出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...");
    }
}

Autowired class that is injected into the test class and whose method is throwing the NPE 自动接线类,该类已插入测试类,并且其方法抛出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
    }   
}

Do not use doAnswer if you simply want to capture the arguments and process/verify them in some way. 如果您只是想捕获参数并以某种方式处理/验证它们,请不要使用doAnswer。 Mockito has a defined feature called ArgumentCaptor that is designed just for that. Mockito具有一个定义为ArgumentCaptor功能,正是为此而设计的。 By using it you will not need to haggle around with that void method the way you do: 通过使用它,您将不需要像以前那样闲逛那个void方法:

@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();
}

Thank you Maciej for the answer. 谢谢Maciej的回答。 Actually I don't want to do anything with the arguments, I just need to skip this method call. 实际上,我不需要对参数做任何事情,只需要跳过此方法调用即可。 I just used doAnswer with some dummy code since doNothing() or doThrow() did not work with this method. 我只是将doAnswer与一些伪代码一起使用,因为doNothing()或doThrow()不适用于此方法。

I was able to resolve the issue however. 但是,我能够解决该问题。 One of the Autowired components (eventsConfigProperties) for the class which was being injected with Mocks (MigrationRequestServiceImpl) was not being mocked in the test class! 测试类中没有模拟被Mocks(MigrationRequestServiceImpl)注入的类的自动装配组件之一(eventsConfigProperties)! Thanks to @daniu for pointing this out. 感谢@daniu指出这一点。

The stack trace from Mockito was not very helpful in debugging the issue, it just gave a null pointer exception right on the method invocation which caused me to think there may be other issues! 来自Mockito的堆栈跟踪对于调试该问题不是很有帮助,它只是在方法调用中给出了空指针异常,这使我认为可能还有其他问题!

Apologize for the mistake, my bad, but thank you and good to know about the ArgumentCaptor, would probably need it for future testing! 抱歉,我很抱歉,但是非常感谢您,并且很高兴了解ArgumentCaptor,以后可能需要使用它!

Had to add this entry which was autowired into the MigrationRequestService class. 必须添加自动连接到MigrationRequestService类中的该条目。

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

    // Other declarations
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM