我在尝试对函数调用进行单元测试时遇到问题。对于 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,如代码注释中所述
@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
}
}
如果您只想捕获参数并以某种方式处理/验证它们,请不要使用 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();
}
谢谢Maciej的回答。实际上我不想对参数做任何事情,我只需要跳过此方法调用。我只是将doAnswer与一些虚拟代码一起使用,因为doNothing((或doThrow((不适用于此方法。
但是,我能够解决问题。正在注入模拟(MigrationRequestServiceImpl(的类的自动连线组件(eventsConfigProperties(之一在测试类中没有被模拟!感谢@daniu指出这一点。
来自 Mockito 的堆栈跟踪对调试问题不是很有帮助,它只是在方法调用上给出了一个空指针异常,这让我认为可能还有其他问题!
为错误道歉,我的错,但谢谢你,很高兴知道 ArgumentCaptor,可能需要它用于未来的测试!
必须添加此条目,该条目已自动连接到MigrationRequestService类中。
// Test class
@RunWith(MockitoJUnitRunner.class)
public class RetriggerRequestTest {
@Autowired
EventsConfigProperties eventsConfigProperties;
// Other declarations
}