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:
- Activity Espresso Test With Dagger’s Android Injector
- Fragment Espresso Testing With Dagger’s Android Injector
- https://github.com/SabagRonen/dagger-activity-test-sample
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
1 | @ActivityKey |
with
1 | @ClassKey |
2. The generic typing for AndroidInjector.Factory and other classes used in the AndroidInjector code have changed from
1 2 | <? extends Activity> // java <out Activity> // kotlin |
to
1 2 | <?> // 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).
01 02 03 04 05 06 07 08 09 10 11 | // 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); |
1 2 3 4 5 6 7 8 9 | // 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.
Published on Java Code Geeks with permission by David Wong, partner at our JCG program. See the original article here: Update Your Fake AndroidInjector Opinions expressed by Java Code Geeks contributors are their own. |