Robolectric 3 with Fabric Crashlytics
O

1

1

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!

Okie answered 1/5, 2016 at 17:27 Comment(1)
try Fabric.class instead of ShadowFabric.class at builder.addInstrumentedClass(ShadowFabric.class.getName());Urinal
G
3

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!

Galegalea answered 2/5, 2016 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.