Поймать исключение Java из Runnable в модульном тесте

У меня есть код ниже

public final class JoinableTaskPool<T> extends ABC {
       private int taskCounter;
       private final Object monitor;
       private final ExtendedThreadPoolExecutor service;
       private final CompletionService<T> compService;

       public Future<T> submit(final Callable<T> task) {
           final Future<T> result = compService.submit(task);
           service.submit(new Runnable() {
             public void run() {
                try {
                    final Future<T> result = compService.take();
                    try {
                        handler.processResult(result);
                    } catch (final Throwable t) {
                        throw new SearchException("Task has an error", t);
                    }
                } catch (InterruptedException e) {
                    throw new SearchException("Task has an error", e);
                }
            }
          } 
          return result;
       }

  public void join() {
    synchronized (monitor) {
        while (taskCounter != 0) {
            try {
                monitor.wait();
            } catch (InterruptedException e) {
                error(e, "Interrupted in join");
            }
        }
  }
  }

Класс ExtendedThreadPoolExecutor определяется следующим образом.

public class ExtendedThreadPoolExecutor extends ThreadPoolExecutor {

    public ExtendedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if(t != null) {
            throw new SearchException("Error while executing the task", t);
        }
    }
}

Я пытаюсь написать модульный тест для этого метода. Ниже приведен метод

@RunWith(MockitoJUnitRunner.class)
public class TestJoinableTaskPool<T> {

    private JoinableTaskPool<T> pool;

    @Before
    public void setUp() {
        pool = new JoinableTaskPool<T>(1);
    }

    @After
    public void tearDown() {
        pool.shutdown();
    }

    @Test (expected = SearchException.class)
    public void testSubmit() throws Exception{
        Callable task = (Callable<T>) () -> null;

        Mockito.when(pool.getFinishHandler().processResult(result))
                .thenThrow(RuntimeException.class);

        pool.submit(task);
    }
}

Поскольку исключение SearchException генерируется в runnable, нет никакого способа получить к нему доступ вне метода отправки. Если бы я вернул Future, возвращенный executorService.submit, я мог бы сделать future.get(), чтобы получить исключение, но я возвращаю другое Future (переменная result). Поэтому при написании модульного теста я не могу получить исключение.

Также я переопределил afterExecute(), чтобы поймать исключение из модульного теста, но не смог найти способ его вызвать.

Как проверить это исключение, выброшенное из исполняемого файла из модульного теста. Любая помощь будет оценена по достоинству.


person BigDataLearner    schedule 13.06.2020    source источник
comment
Зачем нужно отправлять одну и ту же задачу CompletionService и ExtendedThreadPoolExecutor?   -  person No Ordinary Love    schedule 13.06.2020
comment
service -> Базовая служба-исполнитель для отправки compService -> Базовая служба завершения для «присоединения». У меня также есть метод присоединения, который я обновил в вопросе.   -  person BigDataLearner    schedule 14.06.2020


Ответы (1)


Не говоря уже о том, что его код пахнет за версту, вы можете создать дополнительный интерфейс, например

public interface MyErrorHandler { handleError(Exception e)

внедрите его в свои пулы исполнителей и вызовите его в исключении. Затем вы можете использовать use Mockito.spy, чтобы увидеть, был ли этот метод вызван или нет для MyErrorHandler (cglib должен позволить вам сделать это даже без дополнительного интерфейса).

в качестве альтернативы вы можете определить, что экземпляр MyExceptionHandler должен быть передан исполнителю (через конструктор или сеттер), чтобы вы могли обеспечить реализацию такого обратного вызова в зависимости от варианта использования.

person Antoniossss    schedule 14.06.2020