I am trying to ShadowClass Crashlytics/Fabric so that Robotlectric 3 tests do not fail. What I have so far is this:
The custom test runner that adds the Shadow class for Fabric:
public class TestRunner extends RobolectricGradleTestRunner {
public TestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected ShadowMap createShadowMap() {
return super.createShadowMap()
.newBuilder().addShadowClass(ShadowFabric.class).build();
}
@Override
public InstrumentationConfiguration createClassLoaderConfig() {
InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder();
builder.addInstrumentedClass(ShadowFabric.class.getName());
return builder.build();
}
}
The shadow class for Fabric:
@Implements(Fabric.class)
public class ShadowFabric {
@Implementation
public static Fabric with(Context context, Kit... kits) {
System.out.println("Shadowing Fabric");
return null;
}
}
My application class for my app:
public class MyApp extends Application {
@Override
public void onCreate() {
setupCrashlytics();
}
protected void setupCrashlytics() {
Crashlytics crashlyticsKit = new Crashlytics.Builder().core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()).build();
// Initialize Fabric with the debug-disabled crashlytics.
Fabric.with(this, crashlyticsKit);
}
}
And here is the test that passes in Debug (because Crashlytics is disabled on it), but fails in release mode because the ShadowClass is not working correctly:
@RunWith(TestRunner.class)
@Config(constants = BuildConfig.class, sdk=21, packageName="com.my.release.package.name", shadows={ShadowFabric.class})
public class MyTest {
@Test
public void testGreenDAOsave() {
// blah
}
}
The error I get with Crashlytics / Fabric during the test is the following:
STANDARD_ERROR
io.fabric.sdk.android.services.concurrency.UnmetDependencyException: com.crashlytics.android.core.CrashlyticsMissingDependencyException:
This app relies on Crashlytics. Please sign up for access at https://fabric.io/sign_up
install an Android build tool and ask a team member to invite you to this app's organization.
The stack trace shows that MyApp.setupCrashlytics() is being called and Fabric.with() is failing.
1) YES, the app is registered with Crashlytics.
2) YES, I did contact Crashlytics support email. I was told 'Robolectric is not supported'.
From what I can see, I just need to get the shadow class thing working and then Crashlytics will get shadowed and not init'd.
Ideas / Help would be very much appreciated!
This is my usual advice how to write a test against something not testable.
Extract you Fabric initialisation to protected method:
public class <MyApplicationName> {
public void onCreate() {
initFabric();
}
@VisibileForTesting
void initFabric() {
....
}
}
Create Test<MayApplicationName>
class in test sources (same package and override Fabric initialisation:
public class Test<MyApplicationName> {
void initFabric() {
//nothing to do
}
}
Everywhere where you need using Fabric use DI (Dependency Injection) to mock Fabric in tests. Even more, I would suggest you create Analytics/Crash/Distribution class and hide Fabric usage from entire application.
And final you have left classes that wrap/hide the Fabric. Here you can write a custom shadow, spy on the real object or leave it untested. And you already tried to write custom shadow without success, also, spying is not an option here.
Happy coding!