如何测试Quarkus中基于Mutiny Uni实现重试的方法?
问题描述
我正在使用Quarkus、RESTEasy和Mutiny框架,编写了一个调用REST客户端并在失败时重试的方法:
class MyService { private MyRestClient restClient; public Uni<Response> fetchData() { return restClient.fetchData() .onFailure().retry().withBackOff(Duration.ofMillis(100), Duration.ofSeconds(5)) .atMost(5); } }
为验证重试逻辑是否正确,编写了如下测试代码:
@Test public void shouldRetry() { Mockito.when(restClient.fetchData()) .thenReturn( Uni.createFrom().failure(new RuntimeException("Failed to fetch data")), Uni.createFrom().item(Response.ok().build()) ); assertDoesNotThrow(() -> myService.fetchData().await().indefinetely()); Mockito.verify(restClient, Mockito.times(2)).fetchData(); }
测试在assertDoesNotThrow断言处失败:重试逻辑虽触发,但并未调用新的fetchData方法,而是一直复用最初返回异常的同一个Uni,导致重试5次后抛出IllegalStateException: Retries exhausted: 5/5。
问题原因
Mockito的thenReturn方法会直接返回你传入的Uni实例,每次调用restClient.fetchData()都会拿到同一个对象。而Mutiny的重试逻辑是重新订阅已有的Uni实例,不会触发restClient.fetchData()方法的再次调用。第一次返回的失败Uni无论订阅多少次都会抛出异常,自然会耗尽重试次数。
修复方案
改用Mockito的thenAnswer方法,让每次调用restClient.fetchData()都生成并返回新的Uni实例,确保重试时能触发新的调用:
@Test public void shouldRetry() { // 用Answer来每次调用生成新的Uni Mockito.when(restClient.fetchData()) .thenAnswer(invocationOnMock -> { // 模拟第一次调用失败,第二次成功 if (Mockito.mockingDetails(restClient).getInvocations().size() < 1) { return Uni.createFrom().failure(new RuntimeException("Failed to fetch data")); } else { return Uni.createFrom().item(Response.ok().build()); } }); // 修正拼写错误:indefinetely → indefinitely assertDoesNotThrow(() -> myService.fetchData().await().indefinitely()); Mockito.verify(restClient, Mockito.times(2)).fetchData(); }
另外,原测试代码中的await().indefinetely()存在拼写错误,必须修正为await().indefinitely(),否则会编译报错。
补充优化
如果需要更灵活的多轮返回(比如前N次失败,第N+1次成功),可以用thenAnswer链式调用的方式,每次调用都生成新的Uni实例:
@Test public void shouldRetry() { Mockito.when(restClient.fetchData()) // 第一次调用返回失败的新Uni实例 .thenAnswer(inv -> Uni.createFrom().failure(new RuntimeException("Failed"))) // 第二次调用返回成功的新Uni实例 .thenAnswer(inv -> Uni.createFrom().item(Response.ok().build())); assertDoesNotThrow(() -> myService.fetchData().await().indefinitely()); Mockito.verify(restClient, Mockito.times(2)).fetchData(); }
内容的提问来源于stack exchange,提问作者Allan Juan
相关产品推荐
相关产品推荐

