使用Mockito对调用异步函数的函数进行单元测试

时间:2015-12-01 10:22:24

标签: unit-testing junit mockito

我有一个调用异步函数的方法:

public class MyService {
  ...
  public void uploadData() {
    MyPool.getInstance().getThreadPool().execute(new Runnable() {
                @Override
                public void run() {
                    boolean suc = upload();
                }
            });
        }
}

我想用Mockito对此功能进行单元测试,我试过了:

MyPool mockMyPool = Mockito.mock(MyPool.class);
ThreadPool mockThreadPool = Mockito.mock(ThreadPool.class);
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);

when(mockMyPool.getThreadPool()).thenReturn(mockThreadPool);
MyService service = new MyService();

// run the method under test
service.uploadData();

// set the runnableCaptor to hold your callback
verify(mockThreadPool).execute(runnableCaptor.capture());

但我收到了错误:

org.mockito.exceptions.verification.WantedButNotInvoked: 
Wanted but not invoked:
threadPool.execute(
    <Capturing argument>
);

为什么我收到此错误,如何使用Mockito对uploadData()函数进行单元测试?

2 个答案:

答案 0 :(得分:0)

好吧,我自己想办法,因为MyPool是单身人士。我添加了一个公共函数setInstance(mockedInstance)来将模拟的实例传递给MyPool。然后,它的工作原理。我知道它有点“脏”,但如果你有更好的解决方案,请告诉我。谢谢!

答案 1 :(得分:0)

除了保留MyPool或ThreadPool字段的DI方法之外,您还可以重构一点以允许在方法中依赖注入

public class MyService {
  ...
  public void uploadData() {
    uploadData(MyPool.getInstance().getThreadPool());
  }

  /** Receives an Executor for execution. Package-private for testing. */
  void uploadData(Executor executor) {
    executor.execute(new Runnable() {
      @Override public void run() {
        boolean suc = upload();
      }
    });
  }
}

这可能更简洁,因为它将ThreadPool降低到你需要的抽象级别(Executor),这意味着你只是在模拟单方法接口而不是ThreadPool(我假设它与ThreadPoolService有关;否则,你也可以接受一个ThreadPool)。正式地,您的uploadData()将未经过测试,但您可以轻松彻底地测试uploadData(Executor)uploadData(ThreadPool),这些是最有可能破坏的移动部件。

包私有技巧确实依赖于你的代码和测试在同一个包中,尽管它们可能位于不同的源文件夹中;或者,您可以将ThreadPool接收调用作为公共API的一部分,这将在以后提供更大的灵活性。