问题
I want to make sure I'm not doing something terribly wrong before I submit a bug report. This is really weird. The setup:
robolectric 3.0; mockito 1.10.19
Unit under test:
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 );
}
The test:
@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();
}
}
The test fails with:
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)
I repeat: I hit breakpoints inside the method that verify() checks but the test fails. I've also tried creating a dummy method inside of checkForStopped()
and verifying on that to no avail. I've also tried 1000ms thread.sleep on either side of the robolectric UI thread call. My guess is something is going on with an interaction between robolectric's and mockito's reflection stuff?
回答1:
You've caught a really fun bit of expected-but-unintuitive behavior, based on this Mockito principle: To create a spy, Mockito makes a shallow copy of the original object.
When you create the anonymous inner Runnable in the constructor, the Runnable contains an implicit reference to BbScrollView.this
, your original BbScrollView object. Then, you make a copy as you create the spy, and the reference to your original BbScrollView persists. This means that your call to checkForStopped
happens to the original object that Mockito can't observe, not the spy.
One way to fix this is to move your anonymous inner Runnable creation to your startScrollTask
method, invoked on the spy, so this
refers to the spy. When the Runnable is run, it will call methods on the spy instead of the real object, allowing Mockito to intercept and verify the call.
来源:https://stackoverflow.com/questions/33656249/mockito-runnable-wanted-but-not-invoked