【问题标题】:Mockito Junit Test - “Wanted but not invoked; However there were other interactions with this mock” errorMockito Junit 测试 - “需要但未调用;但是还有其他与此模拟“错误的交互
【发布时间】:2019-08-25 20:20:33
【问题描述】:

我必须在 Completable Future 中超时运行函数。只有当原始方法花费的时间超过给定的超时时间时,才应该调用可运行函数。单位不断给= 需要但未调用:但是,与此模拟的交互恰好有 3 次。

我想要做的是,我正在尝试为方法执行添加超时 (getResponseWithTimeoutFunction(itemRequest)),如果该方法需要更多时间,则终止它并发布计数(以了解超时响应率)作为度量。

@Test
public void testTimeoutFunction() throws Exception {
    Response response = getResponseForTest(); 
    when(requestAdapter.transform(itemRequest)).thenReturn(Request);

    when(dataProvider
        .provide(any(Request.class)))
        .thenAnswer((Answer<Response>) invocation -> {
            Thread.sleep(1000000);
            return response;
        });

    processor = spy(getProcessor());

    when(itemRequest.getRequestContext()).thenReturn(itemRequestContext);
    when(itemRequestContext.getMetadata()).thenReturn(requestContextMetadata);

    List<Item> output = processor.getItemist(ITEM_ID, itemRequest);

    assertTrue(output.isEmpty());
    verify(processor, times(1)).processRequest(Request);
    verify(processor, times(1)).responseTimedOutCount();
}

这是我正在测试的方法:

public class Process {

    @VisibleForTesting
    void  responseTimedOutCount() {
    //log metrics
    }

    private CompletableFuture<Response> getResponseAsync(final ScheduledExecutorService delayer,
                                                                             final ItemRequest itemRequest) {
        return timeoutWithTimeoutFunction(delayer, EXECUTION_TIMEOUT, TimeUnit.MILLISECONDS,
                CompletableFuture.supplyAsync(() -> getResponseWithTimeoutFunction(itemRequest), executorService),
                Response.emptyResponse(), () -> responseTimedOutCount());
    }


    private Response getResponseWithTimeoutFunction(final ItemRequest itemRequest) {
        //do something and return response
    }

   public List<Item> getItemList(final String id, final ItemRequest itemRequest) throws Exception {

        final ScheduledExecutorService delayer = Executors.newScheduledThreadPool(1);
        Response response;
        if(validateItemId(id){
            try {
                response = getResponseAsync(delayer, itemRequest).get();
            } catch (final Throwable t) {
                response = Response.emptyResponse();
            } finally {
                delayer.shutdown();
            }
            return transform(response, id).getItems(); 
        } else {
            return null;
        }
    }
   }

而超时功能使用=

public static <T> CompletableFuture<T> timeoutWithTimeoutFunction(final ScheduledExecutorService es,
                                                                      final long timeout,
                                                                      final TimeUnit unit,
                                                                      final CompletableFuture<T> f,
                                                                      final T defaultValue,
                                                                      final Runnable r) {
        final Runnable timeoutFunction = () -> {
            boolean timedOut = f.complete(defaultValue);
            if (timedOut && r != null) {
                r.run();
            }
        };

        es.schedule(timeoutFunction, timeout, unit);
        return f;
    }

来自 Junit 的异常:

   Wanted but not invoked: process.responseTimedOutCount(); -> at processTest.testTimeoutFunction(processTest.java:377) 
   However, there were exactly 3 interactions with this mock: 
   process.getItemList( ITEM_ID, itemRequest ); -> at processTest.testTimeoutFunction(processTest.java:373) 
   process.validateItemId( ITEM_ID ); -> at process.getItemList(process.java:133) 
   process.processRequest( request ); -> at process.getResponseWithTimeoutFunction(process.java:170)

【问题讨论】:

  • Thread.sleep 在测试中总是一个不好的迹象。您如何确定时间完全符合预期?
  • 这就是问题所在。我想测试我的超时功能是否按预期正确使用。
  • 如果希望您不介意我的提问:为什么要在此处使用单独的 ScheduledExecutorService 来处理超时处理,而您可以简单地在 CompletableFuture 上使用 get(timeout, TimeUnit) 代替?我能看到的唯一原因是 responseTimedOutCount 不应该在 main 线程上运行。
  • @second 很抱歉没有意识到这一点。你能简单地告诉我吗?
  • 我已经读过这个tutorial,它使用延迟器使代码非阻塞,但是在这种情况下,在你知道isDone() == true之前你不能真正使用get方法。

标签: java junit java-8 mockito completable-future


【解决方案1】:

要测试超时,您可能需要模拟要测试超时的调用。相对于测试的持续时间,它应该永远持续下去。

when(dataProvider
    .provide(any(Request.class)))
    .thenAnswer((Answer<Response>) invocation -> {
        Thread.sleep(FOREVER);
        return response;
    });

验证应该有一个线程处理超时。当超时时间很长时,您可能应该确保它是可配置的以允许快速测试。类似verify(mock, timeout(LONGER_THAN_REAL_TIMEOUT)).someCall()

确保对总测试持续时间设置超时,以确保当前或未来的故障不会减慢您的构建速度。

【讨论】:

  • FOREVER = 1000000 像这样?
  • 我认为这是因为这个测试在 IntelliJ 中成功但在构建期间没有成功
  • 当然。但是,如果您的测试进行的时间接近那么长,则测试无法正常工作。它应该会失败。
  • 那么,说明进程还在运行,线程没有终止?
  • 嘲讽很容易让人迷惑。请不要道歉。这是个好问题。让我尝试另一种方式。模拟伪造行为。我建议的方法是,如果生产代码中没有超时处理,则始终使测试失败。验证一直等到应该触发超时处理代码。
【解决方案2】:

为了测试异步调用,你可以使用:

verify(processor, timeout(1000)).processRequest(request);

【讨论】:

  • 但这不会在超时的情况下测试是否调用了超时可运行对象。
猜你喜欢
  • 2011-12-18
  • 2013-08-15
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多