mockito对kafkatemplate.send使用thenthorn引发异常

lbsnaicq  于 2021-07-26  发布在  Java
关注(0)|答案(3)|浏览(585)

我正在测试以下功能。

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) 不会引发异常。所以测试总是失败。
请告诉我我遗漏了什么。

sqserrrh

sqserrrh1#

我很高兴您通过将示例传递给构造函数来解决问题。
但是,不是创建正确的构造函数,而是示例化 placeService 在测试方法本身中,我将使用另一种方法。
作为最佳实践,建议使用特定的set方法来传递示例,例如在您的案例中,在placeservice类中,您应该有如下内容:

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

这样,就可以留下模拟对象和 placeService 作为测试类的成员,这样junit和springrunner就有责任示例化这些对象并将它们注入 placeService ,然后您就可以根据需要定制您将编写的每个测试方法的模拟行为。
根据我的经验,我发现这很有帮助,因为每一个涉及的对象都有其适当的工作。即使在测试实现和可维护性方面,您也不必在每个测试方法中重复相同的代码。例如,考虑一下如果在某个时候你必须更改构造函数会发生什么,在这种情况下你也必须更改所有使用它的方法。你不觉得吗?

2hh7jdfx

2hh7jdfx2#

我建议创建失败的 ListenableFuture 对象有异常而不是 Mock ```
SettableListenableFuture<SendResult<String, Object>> future = new SettableListenableFuture<>();
future.setException(new RuntimeException())

然后把这个以假乱真地还给我

Mockito.when(kafkaTemplate.send(Mockito.any(ProducerRecord.class))).thenReturn(listenableFuture);

因此,当调用get方法时,它抛出 `ExecutionException` 如果通过set(object)设置了值,则此方法返回该值;如果通过setexception(throwable)设置了异常,则抛出executionexception;如果取消了未来,则抛出cancellationexception。
hpxqektj

hpxqektj3#

问题是 PlaceService 没有使用的模拟示例 KafkaTemplate . 所以我把mock示例传递给 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));
}

相关问题