在提交错误报告之前,我想确保自己没有做错事。这真的很奇怪。设置:
robolectric 3.0; mockito 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)
我再说一遍:我在验证()检查的方法中遇到了断点,但测试失败了。我也尝试在checkForStopped()
内部创建一个虚拟方法,并对此进行验证无济于事。我还在robolectric UI线程调用的任一侧尝试了1000ms thread.sleep。我的猜测是在robolectric和mockito的反射之间进行交互吗?
答案 0 :(得分:5)
基于这个Mockito原则,你发现了一个非常有趣的预期但不直观的行为:要创建一个间谍,Mockito makes a shallow copy of the original object。
在构造函数中创建匿名内部Runnable时,Runnable包含对{em>原始 BbScrollView对象的BbScrollView.this
的隐式引用。然后,在创建间谍时进行复制,并且对原始BbScrollView的引用仍然存在。这意味着您对checkForStopped
的调用发生在Mockito无法观察到的原始对象上,而不是间谍。
解决此问题的一种方法是将您的匿名内部Runnable创建移动到您在间谍上调用的startScrollTask
方法,因此this
指的是间谍。运行Runnable时,它将调用间谍上的方法而不是真实对象,允许Mockito拦截并验证呼叫。