Mockito 使用 thenThrow 为 KafkaTemplate.send 抛出异常
Mockito throw exception for KafkaTemplate.send using thenThrow
我正在测试以下功能。
public boolean produceNumberOfPeople(NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO) {
final ProducerRecord<Integer, Integer> record = new ProducerRecord<>(
KafkaConfig.NUMBER_OF_PEOPLE_BY_PLACE_TOPIC,
numberOfPeopleInPlaceDTO.getId(),
numberOfPeopleInPlaceDTO.getNumberOfPeople());
try {
kafkaTemplate.send(record).get(2, TimeUnit.SECONDS);
return true;
}
catch (ExecutionException | TimeoutException | InterruptedException e) {
return false;
}
}
这是测试代码。
@Test
public void produceNumberOfPeopleTest() throws InterruptedException, ExecutionException, TimeoutException {
NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO = NumberOfPeopleInPlaceDTO.builder()
.id(1)
.numberOfPeople(10)
.build();
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class)))
.thenReturn(listenableFuture);
Mockito.when(listenableFuture.get(2,TimeUnit.SECONDS))
.thenThrow(TimeoutException.class);
Assert.assertFalse(placeService.produceNumberOfPeople(numberOfPeopleInPlaceDTO));
}
我定义了以下变量。
@Autowired
private PlaceService placeService;
@MockBean
private PlaceRepository placeRepository;
@MockBean
private KafkaTemplate<Integer, Integer> kafkaTemplate;
@MockBean
private ListenableFuture listenableFuture;
问题是 kafkaTemplate.send(record).get(2,TimeUnit.SECONDS)
没有抛出异常。所以测试一直失败。
有什么不足请指教。
我会建议创建失败的 ListenableFuture
对象而不是 Mock
SettableListenableFuture<SendResult<String, Object>> future = new SettableListenableFuture<>();
future.setException(new RuntimeException())
然后 return 模拟
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class))).thenReturn(listenableFuture);
所以当 get 方法被调用时它抛出 ExecutionException
This method returns the value if it has been set via set(Object), throws an ExecutionException if an exception has been set via setException(Throwable), or throws a CancellationException if the future has been cancelled.
问题是 PlaceService
没有使用 KafkaTemplate
的模拟实例。所以我手动将模拟实例传递给 PlaceService
的构造函数。现在测试通过了。
这是新的测试代码。
@Test
public void produceNumberOfPeopleTest() throws InterruptedException, ExecutionException, TimeoutException {
NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO = NumberOfPeopleInPlaceDTO.builder()
.id(1)
.numberOfPeople(10)
.build();
PlaceService testPlaceService = new PlaceServiceImpl(null,kafkaTemplate);
SettableListenableFuture<SendResult<String, Object>> future = new SettableListenableFuture<>();
future.setException(new RuntimeException());
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class))).thenReturn(future);
Assert.assertFalse(testPlaceService.produceNumberOfPeople(numberOfPeopleInPlaceDTO));
}
很高兴您通过将实例传递给构造函数解决了问题。
但是,我不会创建适当的构造函数并在测试方法本身中实例化 placeService
,而是使用另一种方法。
作为最佳实践,建议使用特定的 setXXX 方法来传递实例,例如在您的情况下,在 PlaceService class 中您应该有这样的东西:
public void setListenableFuture(ListenableFuture listenableFuture) {
this.listenableFuture = listenableFuture;
}
public void setKafkaTemplate(KafkaTemplate<Integer, Integer> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
然后,您可以在您的测试方法中调用它们(在您的情况下 produceNumberOfPeopleTest
),或者在特定的设置方法中调用它们更好,如下所示:
@Before
public void setUp() throws Exception {
placeService.setListenableFuture(listenableFuture);
placeService.setKafkaTemplate(kafkaTemplate);
}
通过这种方式,您可以将 Mock 对象和 placeService
作为您的测试成员 class,这样 JUnit 和 Spring Runner 将负责实例化这些对象并且将它们注入 placeService
,然后您可以根据需要自定义每个测试方法的模拟行为。
根据我的经验,我发现这很有帮助,因为涉及的每个对象都各司其职。即使在测试实现和可维护性方面,您也不必在每个测试方法中重复相同的代码。例如,想一想如果在某个时候您必须更改该构造函数会发生什么情况,在这种情况下您将不得不更改您使用它的所有方法。你不觉得吗?
我正在测试以下功能。
public boolean produceNumberOfPeople(NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO) {
final ProducerRecord<Integer, Integer> record = new ProducerRecord<>(
KafkaConfig.NUMBER_OF_PEOPLE_BY_PLACE_TOPIC,
numberOfPeopleInPlaceDTO.getId(),
numberOfPeopleInPlaceDTO.getNumberOfPeople());
try {
kafkaTemplate.send(record).get(2, TimeUnit.SECONDS);
return true;
}
catch (ExecutionException | TimeoutException | InterruptedException e) {
return false;
}
}
这是测试代码。
@Test
public void produceNumberOfPeopleTest() throws InterruptedException, ExecutionException, TimeoutException {
NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO = NumberOfPeopleInPlaceDTO.builder()
.id(1)
.numberOfPeople(10)
.build();
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class)))
.thenReturn(listenableFuture);
Mockito.when(listenableFuture.get(2,TimeUnit.SECONDS))
.thenThrow(TimeoutException.class);
Assert.assertFalse(placeService.produceNumberOfPeople(numberOfPeopleInPlaceDTO));
}
我定义了以下变量。
@Autowired
private PlaceService placeService;
@MockBean
private PlaceRepository placeRepository;
@MockBean
private KafkaTemplate<Integer, Integer> kafkaTemplate;
@MockBean
private ListenableFuture listenableFuture;
问题是 kafkaTemplate.send(record).get(2,TimeUnit.SECONDS)
没有抛出异常。所以测试一直失败。
有什么不足请指教。
我会建议创建失败的 ListenableFuture
对象而不是 Mock
SettableListenableFuture<SendResult<String, Object>> future = new SettableListenableFuture<>();
future.setException(new RuntimeException())
然后 return 模拟
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class))).thenReturn(listenableFuture);
所以当 get 方法被调用时它抛出 ExecutionException
This method returns the value if it has been set via set(Object), throws an ExecutionException if an exception has been set via setException(Throwable), or throws a CancellationException if the future has been cancelled.
问题是 PlaceService
没有使用 KafkaTemplate
的模拟实例。所以我手动将模拟实例传递给 PlaceService
的构造函数。现在测试通过了。
这是新的测试代码。
@Test
public void produceNumberOfPeopleTest() throws InterruptedException, ExecutionException, TimeoutException {
NumberOfPeopleInPlaceDTO numberOfPeopleInPlaceDTO = NumberOfPeopleInPlaceDTO.builder()
.id(1)
.numberOfPeople(10)
.build();
PlaceService testPlaceService = new PlaceServiceImpl(null,kafkaTemplate);
SettableListenableFuture<SendResult<String, Object>> future = new SettableListenableFuture<>();
future.setException(new RuntimeException());
Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class))).thenReturn(future);
Assert.assertFalse(testPlaceService.produceNumberOfPeople(numberOfPeopleInPlaceDTO));
}
很高兴您通过将实例传递给构造函数解决了问题。
但是,我不会创建适当的构造函数并在测试方法本身中实例化 placeService
,而是使用另一种方法。
作为最佳实践,建议使用特定的 setXXX 方法来传递实例,例如在您的情况下,在 PlaceService class 中您应该有这样的东西:
public void setListenableFuture(ListenableFuture listenableFuture) {
this.listenableFuture = listenableFuture;
}
public void setKafkaTemplate(KafkaTemplate<Integer, Integer> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
然后,您可以在您的测试方法中调用它们(在您的情况下 produceNumberOfPeopleTest
),或者在特定的设置方法中调用它们更好,如下所示:
@Before
public void setUp() throws Exception {
placeService.setListenableFuture(listenableFuture);
placeService.setKafkaTemplate(kafkaTemplate);
}
通过这种方式,您可以将 Mock 对象和 placeService
作为您的测试成员 class,这样 JUnit 和 Spring Runner 将负责实例化这些对象并且将它们注入 placeService
,然后您可以根据需要自定义每个测试方法的模拟行为。
根据我的经验,我发现这很有帮助,因为涉及的每个对象都各司其职。即使在测试实现和可维护性方面,您也不必在每个测试方法中重复相同的代码。例如,想一想如果在某个时候您必须更改该构造函数会发生什么情况,在这种情况下您将不得不更改您使用它的所有方法。你不觉得吗?