CompletableFuture SupplyAsync 的 JUnit 测试

问题描述 投票:0回答:1

如何为以下函数模拟和编写单元测试?我发现为 CompletableFuture 编写单元测试很困难。如果函数在3分钟内没有取消任务,我想在单元测试中抛出异常

private void cancel() throws TimeoutException, ExecutionException, InterruptedException {
    try {
      CompletableFuture.supplyAsync(() -> {
        try {
          cancelTask();// function that cancels a task
        } catch (ApiException ex) {
          Logger.error("Api exception while cancelling a tasks")
        }
        return null
      }).get(3, TimeUnit.MINUTES);
    } catch (final InterruptedException | ExecutionException | TimeoutException e) {
      throw e;
    }
  }
java multithreading executorservice completable-future futuretask
1个回答
0
投票

要在调用 cancelTask() 后强制 CompletableFuture.supplyAsync 在单元测试用例中返回超时异常,您必须在测试此用例时重写 cancelTask。您可以通过多种方式做到这一点:

  1. 添加一个字段来通过构造函数设置超时值,设置非常低(在UnitTest期间等待3分钟不好),设置cancelTask函数受保护并在测试用例中覆盖它。

商务舱

    @Slf4j
    @AllArgsConstructor
    public class TaskManager {
        private final int cancelTimeoutInMillis;
    
        public void cancel() throws TimeoutException, ExecutionException, InterruptedException {
            CompletableFuture.supplyAsync(() -> {
                try {
                    cancelTask();// function that cancels a task
                } catch (ApiException ex) {
                    log.error("Api exception while cancelling a tasks");
                }
                return null;
            }).get(cancelTimeoutInMillis, TimeUnit.MILLISECONDS);
        }
    
        @StandardException
        public static class ApiException extends RuntimeException {
        }
    
        protected void cancelTask() {
            //cancel the task
        }
    }

测试班:

    @Slf4j
    class TaskManagerTest {
        @Test
        public void testCancel() throws Exception {
    
            // Create the CompletableFuture under test
            TaskManager manager = new TaskManager(500) {
                @Override
                protected void cancelTask() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
    
            // Verify that the expected exception is thrown
            Assertions.assertThatThrownBy(manager::cancel).isInstanceOf(TimeoutException.class);
        }
    
    }
  1. 通过依赖注入将cancelTask与异步执行器分离并模拟它(或覆盖cancelTask)

商务课程

@AllArgsConstructor
@Getter
public class TaskExecutor {
    private final int cancelTimeoutInMillis;

    public void cancelTask() throws TaskManager.ApiException {
    }
}

@Slf4j
@AllArgsConstructor
public class TaskManager {
    private final TaskExecutor taskExecutor;

    public void cancel() throws TimeoutException, ExecutionException, InterruptedException {
        CompletableFuture.supplyAsync(() -> {
            try {
                taskExecutor.cancelTask();// function that cancels a task
            } catch (ApiException ex) {
                log.error("Api exception while cancelling a tasks");
            }
            return null;
        }).get(taskExecutor.getCancelTimeoutInMillis(), TimeUnit.MILLISECONDS);
    }

    @StandardException
    public static class ApiException extends RuntimeException {
    }
}

测试班

@Slf4j
class TaskManagerTest {
    @Test
    public void testCancel() throws Exception {

        // Create the CompletableFuture under test
        TaskExecutor taskExecutor = new TaskExecutor(500){
            @Override
            public void cancelTask() throws TaskManager.ApiException {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };

        TaskManager manager = new TaskManager(taskExecutor);

        // Verify that the expected exception is thrown
        Assertions.assertThatThrownBy(manager::cancel).isInstanceOf(TimeoutException.class);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.