Mockito creates a proxy instance when some thing is spied on. Now, is there any way to forward setters that are then executed on that proxy instance to the real instance that sits behind it?
Rationale: I have an object instance that I do not have completely under my control, i.e. an Android activity. I can give most parts of my app the proxied version and that runs fine as is, but because I need to create the spy / proxy very early during the creation phase of the activity, it is not yet fully instantiated, e.g. the base context is not attached. This happens on the proxy instance and is of course not used by the activity instance itself (which refers to itself via Activity.this
). The end result is that this leads to all kinds of crashes because resource resolving happens via this base context, so the internal Fragment machinery throws NPEs and more.
Here is some code:
public class CustomAndroidJUnitRunner extends AndroidJUnitRunner {
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Activity activity = super.newActivity(cl, className, intent);
return maybeStubSomeDelegate(activity);
}
private Activity maybeStubSomeDelegate(Activity activity) {
if (!(activity instanceof SomeDelegate)) {
return activity;
}
Activity spiedActivity = spy(activity);
doReturn(SomeDelegateMock.getInstance())
.when((SomeDelegate) spiedActivity)
.getDelegate();
return spiedActivity;
}
}
I'm clueless - any ideas?
android Test Support library's SingleActivityFactory, ActivityTestRule and Mockito's spy()
dependencies {
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'org.mockito:mockito-android:2.21.0'
}
inject the spied instance inside SingleActivityFactory's implementation
public class MainActivityTest {
MainActivity subject;
SingleActivityFactory<MainActivity> activityFactory = new SingleActivityFactory<MainActivity>(MainActivity.class) {
@Override
protected MainActivity create(Intent intent) {
subject = spy(getActivityClassToIntercept());
return subject;
}
};
@Rule
public ActivityTestRule<MainActivity> testRule = new ActivityTestRule<>(activityFactory, true, true);
@Test
public void activity_isBeingSpied() {
verify(subject).setContentView(R.layout.activity_main);
}
}
You can use Robolectric to create your own proxy (or as Robolectric calls them "Shadows") to your activity,
When you create the proxy you can create your own setters that can trigger the real object methods,
How to create a shadow example:
@Implements(Bitmap.class)
public class MyShadowBitmap {
@RealObject private Bitmap realBitmap;
private int bitmapQuality = -1;
@Implementation
public boolean compress(Bitmap.CompressFormat format, int quality, OutputStream stream) {
bitmapQuality = quality;
return realBitmap.compress(format, quality, stream);
}
public int getQuality() {
return bitmapQuality;
}
}
}
when the @RealObject is your real instance,
To use this shadow using Robolectric test runner define a new test class as follows:
@RunWith(RobolectricTestRunner.class)
@Config(shadows = MyShadowBitmap.class)
public class MyTestClass {}
To pull the current shadow instance use the method:
shadowOf()
And in any case, here is s link to Robolectric:
http://robolectric.org/custom-shadows/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With