Mock Away Android Application Dependencies – Yammer Engineering – Medium


Daniel Robertson

At Yammer we run a full suite of tests against our Android application dozens—sometimes hundreds—of times per day. Our goal is to have a feedback loop so fast that our developers are made aware of any breaking changes they’ve made near instantaneously.

Third party libraries that rely on static function calls in an application can be undesirable for testing. They can be a source of slowness, depending on the weight of the given library, or they can pollute analytics. Often times, specific debug-only libraries will provide a no-op version which can be specified in Gradle to avoid execution during tests. One such library is LeakCanary by Square. But it’s rare among libraries designed to run in both debug and production versions of an app. This article will detail how we removed our dependency on Microsoft’s Application Insights analytics library.

Espresso + Dagger

We utilize Espresso and Dagger to accomplish this.

Dagger — a fast dependency injector provided by Square which we use extensively throughout our app.

Espresso — a testing framework provided by Google which we’ve used to create almost 600 tests.

Unfortunately for us, all of the testing code for Espresso runs after Application has already been created. So how did we address this? Simple — we wrote our own TestRunner and extended Application to allow us to specify Dagger modules prior to injection. The TestRunner is responsible for the creation, monitoring, and execution of the application under test.

The below illustration shows how it works.

Now Wrap It

Next, we use Dagger to help us inject an easily mockable wrapper. The new wrapper should look like this:

public class ApplicationInsightsWrapper {
    private Application application; 
    @Inject
    public ApplicationInsightsWrapper(Application application) {
        this.application = application; 
    }
    public void setup() {
       ApplicationInsights.setup(application.getApplicationContext()
           , application);
       ApplicationInsights.start();
    }
}

Now we’ve got something we can mock! Next, we need to override this object in our tests using Dagger. We’ll do that by creating a new module and using Mockito to return a mock object.

@Module(
        injects = {
                TestExampleApplication.class
        },
        complete = false,
        library = true,
        overrides = true)
public class ApplicationInsightsWrapperOverrideModule {
    @Provides
    ApplicationInsightsWrapper provideApplicationInsightsWrapper() {
        return mock(ApplicationInsightsWrapper.class);
    }
}

The Conclusion

Take control of Application by using Dagger to help inject its dependencies. By using this pattern, we improved our Espresso testing speed and reliability. The conscious tradeoff that must be made, however, is the decision as to whether or not that behavior should be tested in the first place. While expensive network calls and analytics can conveniently be removed from Espresso tests, it’s still important to maintain some form of end-to-end testing to ensure all production code gets tested. Otherwise, mock away!

To see how this works in a complete project, view our sample application at GitHub.



Source link