【问题标题】:Mockito runnable: wanted but not invoked?Mockito 可运行:想要但未调用?
【发布时间】:2015-11-11 17:26:41
【问题描述】:

在提交错误报告之前,我想确保自己没有做任何严重错误的事情。这真的很奇怪。设置:

机器人电动 3.0;模拟 1.10.19

待测单元:

public BbScrollView( Context context ){
  this( context, null );
}

public BbScrollView( Context context, AttributeSet attrs ) {
  super( context, attrs );

  mScrollTask = new Runnable() {

    public void run() {
      checkForStopped();
    }
  };
}

public void checkForStopped(){
  int newPosition = getScrollY();
  // the rest is irrelevant , but I hit a breakpoint here.
}

public void startScrollTask() {
  mInitialPosition = getScrollY();
  postDelayed( mScrollTask, mTimeUntilNextCheckForStopped );
}

测试:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class BbScrollViewTests {

  @Test
  public void test_startScrollTask(){
    BbScrollView uut = spy( new BbScrollView( RuntimeEnvironment.application ) );

    // This calls the method above that enqueues the runnable
    uut.startScrollTask();

    // Robolectric runs the runnable
    ShadowLooper.runUiThreadTasksIncludingDelayedTasks();

    // I can hit a breakpoint inside this method but verify() fails
    verify( uut ).checkForStopped();
  }
}

测试失败:

Wanted but not invoked:
bbScrollView.checkForStopped();
-> at com.myapp.android.BbKit.test.view.BbScrollViewTests.test_startScrollTask(BbScrollViewTests.java:62)

However, there were other interactions with this mock:
bbScrollView.startScrollTask();
-> at com.myapp.android.BbKit.test.view.BbScrollViewTests.test_startScrollTask(BbScrollViewTests.java:58)

bbScrollView.getScrollY();
-> at com.myapp.android.BbKit.test.view.BbScrollViewTests.test_startScrollTask(BbScrollViewTests.java:58)

bbScrollView.$$robo$getData();
-> at com.myapp.android.BbKit.test.view.BbScrollViewTests.test_startScrollTask(BbScrollViewTests.java:58)

bbScrollView.postDelayed(
    com.myapp.android.BbKit.view.BbScrollView$1@7f830761,
    100
);
-> at com.myapp.android.BbKit.test.view.BbScrollViewTests.test_startScrollTask(BbScrollViewTests.java:58)

bbScrollView.$$robo$getData();
-> at com.myapp.android.BbKit.test.view.BbScrollViewTests.test_startScrollTask(BbScrollViewTests.java:58)

我再说一遍:我在 verify() 检查的方法中打断点,但测试失败。我还尝试在 checkForStopped() 内部创建一个虚拟方法并对其进行验证,但无济于事。我还在 robolectric UI 线程调用的任一侧尝试了 1000ms thread.sleep。我的猜测是 robolectric 和 mockito 的反射材料之间的相互作用正在发生什么?

【问题讨论】:

    标签: android mockito robolectric


    【解决方案1】:

    根据 Mockito 原则,您发现了一些非常有趣但不直观的行为:要创建间谍,Mockito makes a shallow copy of the original object。

    当您在构造函数中创建匿名内部 Runnable 时,Runnable 包含对 BbScrollView.this 的隐式引用,您的 原始 BbScrollView 对象。然后,您在创建间谍时制作副本,并且对原始 BbScrollView 的引用仍然存在。这意味着您对 checkForStopped 的调用发生在 Mockito 无法观察到的原始对象上,而不是间谍。

    解决此问题的一种方法是将您的匿名内部 Runnable 创建移动到您的 startScrollTask 方法,在间谍上调用,因此 this 指的是间谍。当 Runnable 运行时,它将调用 spy 而不是真实对象上的方法,从而允许 Mockito 拦截并验证调用。

    【讨论】:

    • 哇。一百万年后我永远不会发现这一点。向你致敬,先生。你为我节省了很多时间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多