Mockito:验证来自内部匿名的方法调用 class

Mockito: verifying method call from internal anonymous class

我有一个正在测试的 class,其中包含一个具有内部匿名 class 的方法。匿名class中的一个方法调用了被测class中的一个方法,但是Mockito似乎没有意识到这一点。

public class ClassUnderTest {
    Dependency dependency;
    public ClassUnderTest(Dependency d) {
        dependency = d;
    }
    public void method() {
        dependency.returnsObservable().observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io()).subscribe(new Observer<SupportClass> {

            /* Other methods omitted */
            public void onComplete() {
                 outerMethod();
            })
    }

    public void outerMethod() {
        blah;
    }
}

我的测试代码:

public class TestClass {

    ClassUnderTest underTest;
    Dependency dependency;

    @Before
    public void setUp() throws Exception {

        dependency = Mockito.mock(Dependency.class);
        underTest = Mockito.spy(new ClassUnderTest(dependency));

    }

    @Test
    public void method() throws 
        Mockito.when(dependency.returnObservable()).thenReturn(Observable.just(new SupportClass());

        Mockito.doNothing().when(underTest).outerMethod();

        underTest.method();
        Mockito.verify(underTest).outerMethod();

    }

}

出于某种我似乎无法弄清楚的原因,Mockito 无法检测到正在调用 outerMethod(),即使我已经通过调试器中的逐行单步执行手动验证。我还验证了对依赖对象 returns 的调用是具有正确内容的正确可观察对象,并且确实调用了 onComplete() 和 outerMethod() 方法。我只是很困惑为什么 Mockito 没有检测到它。

这是它吐出的错误:

Wanted but not invoked:
classUnderTest.outerMethod();
-> at (file and line number)

However, there was exactly 1 interaction with this mock:
classUnderTest.method();
-> at (file and line number)

有什么明显的我遗漏的吗?

您正在调度程序之间切换,因此在测试时可能会导致一些问题(您的代码可能会到达 verify 方法 调用实际方法之前

检查 this article 解释如何使用 RxJava 和 Mockito 测试异步代码

TL;DR

添加一个 TestRule 将所有调度程序设置为 trampoline 以便它同步运行:

public class TrampolineSchedulerRule implements TestRule {
  @Override
  public Statement apply(final Statement base, Description d) {
    return new Statement() {
      @Override
      public void evaluate() throws Throwable {
        RxJavaPlugins.setIoSchedulerHandler(
            scheduler -> Schedulers.trampoline());
        RxJavaPlugins.setComputationSchedulerHandler(
            scheduler -> Schedulers.trampoline());
        RxJavaPlugins.setNewThreadSchedulerHandler(
            scheduler -> Schedulers.trampoline());
        RxAndroidPlugins.setInitMainThreadSchedulerHandler(
            scheduler -> Schedulers.trampoline());

        try {
          base.evaluate();
        } finally {
          RxJavaPlugins.reset();
          RxAndroidPlugins.reset();
        }
      }
    };
  }
}