【问题标题】:Mocking void method returning a null pointer exception模拟 void 方法返回空指针异常
【发布时间】:2019-08-15 18:23:20
【问题描述】:

我在尝试对函数调用进行单元测试时遇到问题。对 void 方法调用 messageProducer.sendMessage() 的调用失败,即使它已被存根。

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

我什至尝试了doThrow()doNothing() 存根的其他选项,但在调用存根方法时它们也因相同的 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,如代码 cmets 中所示

@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
    }   
}

【问题讨论】:

  • eventsConfigProperties 来自哪里?看起来它们应该为空。
  • 请添加完整的堆栈跟踪。它不太清楚NPE 来自哪里以及哪个对象为空。
  • 感谢@daniu 指出这一点。正如我在下面的回答中给出的那样,我忽略了模拟这个自动装配的组件。

标签: java unit-testing mocking mockito


【解决方案1】:

如果您只是想捕获参数并以某种方式处理/验证它们,请不要使用 doAnswer。 Mockito 有一个名为ArgumentCaptor 的已定义功能,专为此而设计。通过使用它,您将无需像您那样使用 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();
}

【讨论】:

    【解决方案2】:

    感谢 Maciej 的回答。实际上我不想对参数做任何事情,我只需要跳过这个方法调用。我只是将 doAnswer 与一些虚拟代码一起使用,因为 doNothing() 或 doThrow() 不适用于此方法。

    不过,我能够解决这个问题。使用 Mocks (MigrationRequestServiceImpl) 注入的类的 Autowired 组件之一 (eventsConfigProperties) 没有在测试类中被模拟!感谢@daniu 指出这一点。

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

    为这个错误道歉,我的错,但谢谢你,很高兴了解 ArgumentCaptor,以后的测试可能需要它!

    必须添加这个自动装配到 MigrationRequestService 类中的条目。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-01
      • 1970-01-01
      相关资源
      最近更新 更多