mockito 为什么在执行Future模拟对象的方法时不能抛出异常?

omjgkv6w  于 2022-11-08  发布在  其他
关注(0)|答案(1)|浏览(238)

我正在尝试编写一个测试用例,需要覆盖catch块。catch块捕获InterruptedExceptionExecutionException,两者都在Future对象的.get()方法的方法签名中。我正在尝试使用Mockito中的thenThrow模拟一个InterruptedException,当Future对象的.get()方法被调用时,因此控件进入未覆盖的catch块。
futureData是List<Future<Class>>类型,结果是Future<Class>对象。

futureData.forEach(result -> {
    try {
   enginesData.add(result.get()); // the method I am using to force an exception using Mockito
    } catch (InterruptedException | ExecutionException e) {
   // the catch block I am trying to cover
    }
});

下面是我的测试用例:

@Test
public void testInterruptedException () throws ExecutionException, InterruptedException {
    ...
    InterruptedException interruptedException = new InterruptedException("Interrupted Exception");
    when(oneFutureData.get()).thenThrow(CompletableFuture.completedFuture(interruptedException));
    ...
}

我试着连续写thenThrowdoThrow,但是仍然没有覆盖catch块。这是不是因为我试图模拟一个Future对象和一个Future类的方法?

6qfn3psc

6qfn3psc1#

你需要在你的// the catch block I am trying to cover-块中做一些事情。例如,你可以调用一些记录器来把异常写到日志文件中,或者做其他事情。
诀窍是你需要监视你在// the catch block I am trying to cover中调用的任何东西
然后,您可以检查这个方法是否真的在您的catch区块中呼叫。
看看这个例子,如何实现这一点:

package de.playground.so74236327;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static org.mockito.Mockito.*;
// see https://stackoverflow.com/questions/74236327/why-cant-i-throw-an-exception-when-a-future-mocked-objects-method-is-executed
@ExtendWith(MockitoExtension.class) // needed for the @Mock annotation
public class FutureExceptionTest {
    @Mock
    public Future<Class> futureMock;
    @Test
    public void testInterruptedException() throws ExecutionException, InterruptedException {
        MyService mySpy = spy(new MyService());
        ExampleProductionCode exampleProductionCode = new ExampleProductionCode(mySpy);// contains 2 real Future<Class> objects.
        List<Future<Class>> futureData = exampleProductionCode.getFutureData();
        InterruptedException interruptedException = new InterruptedException("Interrupted Exception");
        // Future<Class> oneFutureData = Mockito.mock(Future.class);
        //when(oneFutureData.get()).thenThrow(CompletableFuture.completedFuture(interruptedException)); that doesn't even compile
        when(futureMock.get()).thenThrow(interruptedException);
        futureData.add(futureMock); // add another Future<Class> object, but this time it's just a mock object
        verify(mySpy, times(0)).logError(interruptedException);//nothing has happend yet
        exampleProductionCode.collectAsyncResults(); // calling the 3 Future.get() methods inside
        // 3 cases:
        // 1st: with the completableFuture.completeExceptionally(e);
        // 2nd: FutureTask<Class> throwing an exception inside the task
        // 3rd: comes from the  oneFutureData mock object above
        verify(mySpy, times(3)).logError(any(Exception.class));
    }
}

您的实际执行程式码看起来会与下列几行类似:

package de.playground.so74236327;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ExampleProductionCode {
    List<Future<Class>> futureData;
    EnginesData enginesData;
    MyService myService;
    public ExampleProductionCode(MyService myService) {
        this.myService = myService;
        futureData = new ArrayList<Future<Class>>();
        enginesData = new EnginesData();
        Future<Class> oneFutureData = createAndStartAsyncTask();
        futureData.add(oneFutureData);
        Future<Class> twoFutureData = createAndStartFutureTask();
        futureData.add(twoFutureData);
    }
    public EnginesData getEngineData() {
        return enginesData;
    }
    public List<Future<Class>> getFutureData() {
        return futureData;
    }
    public Future<Class> createAndStartAsyncTask() {
        CompletableFuture<Class> completableFuture = new CompletableFuture<>();
        Executors.newCachedThreadPool().submit(() -> {
            try {
                Thread.sleep(500);
                // and do whatever you need to do or throw an exception in case something went wrong
                throw new InterruptedException(); // just the exceptional case here for demonstration purposes
                //return null; // return normally if there would be not exception
            } catch (Exception e) {
                completableFuture.completeExceptionally(e);
                // don't call  completableFuture.cancel(false )! that wouldn't work
                // don't call  completableFuture.cancel(true )! that wouldn't work either
            }
        });
        return completableFuture;
    }
    public Future<Class> createAndStartFutureTask() {
        FutureTask<Class> ftask = new FutureTask<Class>(() -> {
            Thread.sleep(500);
            throw new InterruptedException(); // just the exceptional case here for demonstration purposes
            //return MySOClass.class; // return normally if there would be not exception
        });
        ftask.run();
        return ftask;
    }
    public void collectAsyncResults() {
        ExampleProductionCode.EnginesData enginesData = this.getEngineData();
        futureData.forEach(result -> {
            try {
                enginesData.add(result.get()); // the method I am using to force an exception using Mockito
            } catch (InterruptedException | ExecutionException e) {
                // "the catch block I am trying to cover"
                // if you want to cover this catchblock you need to
                // 1. execute some method here, and then
                // 2. spy on this method, so you can check in you test, whether this method was really called with the correct parameters
                myService.logError(e);
            }
        });
    }
    public class EnginesData {
        public void add(Class aclass) {
            //do something
        }
    }
}

package de.playground.so74236327;

public class MyService {
        public void logError(Exception e) {
            //do something
        }
}

你自己试试:
git clone --depth 1 --branch so74236327 git@github.com:bodote/playground.git查看package de.playground.so74236327并运行FutureExceptionTest

相关问题