Update Your Fake AndroidInjector

Using Dagger 2 for dependency injection means that you can inject fake / mock objects for testing. I had been using a fake AndroidInjector to do this in my Espresso tests for an old Android app I was updating, but found that on updating Dagger to a more recent version (from 2.14 to 2.21) the custom AndroidInjector no longer compiled.

The fake AndroidInjector I was using was based on these blogs that were written a couple of years ago:

Another version of that idea can be found here:

This is a quick post on how this can be fixed for anyone who is still using the code from these articles (also a  belated thank- you to these authors for coming up with these ideas).

It Does Not Compile …

The problem came with these internal changes to the Dagger code in version 2.19.

https://github.com/google/dagger/releases/tag/dagger-2.19

Simple Fixes

Based on the code from the articles mentioned above, just need some simple changes (that unfortunately took me a while to work out!).
1. Replace the deprecated annotation

@ActivityKey

with

@ClassKey

2. The generic typing for AndroidInjector.Factory and other classes used in the AndroidInjector code have changed from

<? extends Activity> // java
<out Activity> // kotlin

to

<?> // java
<*> // kotlin

3. The DispatchingAndroidInjector  has changed it’s  constructor signature. Instead of having a single parameter of a Map of the Provider  of the AndroidInjector.Factory keyed by class, there is now an additional parameter of a Map using a String (class name) as the key

So in the Dagger generated code (and in the fake AndroidInjector), the method DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector() also needs this extra parameter (even if just an empty Map).

// java (pseudo-code)

Map<Class<?>, Provider<AndroidInjector.Factory<?>>> classMap = new HashMap<>(1);
// create a custom AndroidInjector.Factory and add it to the provider
Provider<AndroidInjector.Factory<?>> provider = ...
map.put(MyActivity.class, provider);

// empty map to satisfy method signature for newDispatchingAndroidInjector()
Map<String, Provider<AndroidInjector.Factory<?>>> stringMap = new HashMap<>();

return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(classMap, stringMap);
// kotlin

val classMap : Map<Class<*>, Provider<AndroidInjector.Factory<*>>> = mapOf(
Pair<Class<*>, Provider<AndroidInjector.Factory<*>>>(T::class.java, Provider { factory }))

// empty map to satisfy method signature for newDispatchingAndroidInjector()
val stringMap : Map<String, Provider<AndroidInjector.Factory<*>>> = emptyMap<String, Provider<AndroidInjector.Factory<*>>>()

return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector<Activity>(classMap, stringMap)

Why use a fake AndroidInjector?

The more common way to have Dagger inject test dependencies is to maintain a parallel universe of test components and test modules to provide the fake dependencies. This works fine and has the advantages that it shouldn’t break due to internal changes in the Dagger code. However it also means more boilerplate code to maintain.

 

One thought on “Update Your Fake AndroidInjector”

Leave a Reply

Your email address will not be published. Required fields are marked *