Port from Dagger 2 to Android Dagger 2.11 - android

I cannot get my head around how to setup a couple of things when porting the code from Dagger 2 to Android Dagger 2.11. In Dagger 2 the setup was something like this:
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
this.initializeInjector();
}
private void initializeInjector() {
//this class should be auto-generated by Dagger on build
this.applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.netModule(new NetModule())
.build();
}
public ApplicationComponent getApplicationComponent() {
return this.applicationComponent;
}
}
You then had access to the applicationComponent and you could inject any object by using the:
getApplicationComponent().inject(MyCustomObject);
In Android Dagger 2.11 you don't do that anymore. For Activities and Fragments you can inject with the AndroidInjector.inject() method but what about other types? Let's look at an example below. We have a JobManager that can post jobs. The jobs are persisted to a file and when they should be posted they are first deserialized. The problem is that its dependencies are not set of course. So the question is: How to do that?
public class JobManager {
private Context context;
#Inject
public JobManager(Context context) {
this.context = context;
}
public void postJob(String jobId) {
MyJob myJob = deserializePersistedJobFromFile(jobId);
//((App) context).getApplicationComponent().inject(myJob); //This was the old way of doing injection
AndroidInjector.inject(myJob); //This doesn't work - what to do now?
}
.
.
.
}
public class MyJob {
#Inject
ApiService apiService;
.
.
.
}

You can get Dagger to inject a MembersInjector<T> and then use that to inject dependencies into your own objects...
public class JobManager {
private Context context;
#Inject
public JobManager(Context context, MembersInjector<MyJob> jobInjector) {
this.context = context;
}
public void postJob(String jobId) {
MyJob myJob = deserializePersistedJobFromFile(jobId);
jobInjector.inject(myJob);
}
.
.
.
}

Is there any problem you experience with the "old" approach that makes you want to move to the "new" one?
I couldn't find one single real advantage in performing dependency injection using the static AndroidInjector class, but it does increase the complexity of the code.
Therefore, if you don't have a very specific reason to move in that direction, I would suggest to stay with the "old" working approach.
As for injection into non-Activity/Fragment classes, I think you shouldn't use DI framework for this. Use Dagger for injection into Application, Activity, Fragment and Service only. More information available in this post: Dependency Injection in Android.

Writing this on a phone so apologies for any typos.
TL;DR Would it not make sense that, MyJob needs an #Inject(ed) constructor that takes the ApiService, rather than a member injection? Is there a reason why MyJob can't have it's own constructor? (Unfamiliar if this is an Android SDK class or not). If that's not the answer then I have another observation that your JobManager seems to do what the JobScheduler does? Unless it's simply a confusion of terms (i.e. both "jobs")
Outside of Android and it's lifecycle uniqueness dependency inversion's most common use is constructor injection. Learning the DI pattern within the Android framework isn't the best introduction and you get caught up in the 'lying to the complier' DI framework functionality that is meant to simply help move off a legacy codebase (c.f. Dagger 1's static injection or Guide's 'private' member injection).
As a side note, I have avoided moving to "Android Dagger" since it seems to go against having modular separation of code. I think I have more to learn here but right now the boilerplate distruption advantage of "Android Dagger" is outweighed by my need for loosely coupled feature modules to support Instant Apps and an altogether more modular code base.
Hope that helps a bit.

I wrote this article which should be helpful to you. It links to the sample code on Github.
https://proandroiddev.com/exploring-the-new-dagger-android-module-9eb6075f1a46
Pay close attention to the AppModule and how it keeps track of the application so you can pass application.getApplicationContext() into constructors for your JobManager

Using dagger android library has advantages :
1) Using dagger violates dependency injection principle that classes shouldn't know about how dependencies are injected, this violation can be fixed using dagger android library
2) Dagger code can be decoupled from android components.
3) Clean code can be achieved as you don't need to initialize dagger components and call inject methods in android components, leading to creation of easy to maintain apps.
You can decouple dagger from android using dagger android components such as DaggerActivity,DaggerApplication, and DaggerFragment and moving AndroidInjection.inject() calls to central location, instead of calling in each and every activity and fragment, by using activity lifecycle callback on application.
You can read detailed explanation about dagger android at
http://www.zoftino.com/android-framework-classes-dependency-injection-using-dagger-android

Related

Dagger2 - How to conditionally choose modules at runtime

I have a BIG Android app that needs to run different code for depending on the OS version, the manufacturer, and many other things. This app however needs to be a single APK. It needs to be smart enough at runtime to determine which code to use. Until now we have been using Guice but performance issues are causing us to consider migrating to Dagger. However, I've been unable to determine if we can achieve the same use case.
The main goal is for us have some code that runs at startup to provide a list of compatible Modules. Then pass that this list to Dagger to wire everything up.
Here is some pseudocode of the current implementation in Guice we want to migrate
import com.google.inject.AbstractModule;
#Feature("Wifi")
public class WifiDefaultModule extends AbstractModule {
#Override
protected void configure() {
bind(WifiManager.class).to(WifiDefaultManager.class);
bind(WifiProcessor.class).to(WifiDefaultProcessor.class);
}
}
#Feature("Wifi")
#CompatibleWithMinOS(OS > 4.4)
class Wifi44Module extends WifiDefaultModule {
#Override
protected void configure() {
bind(WifiManager.class).to(Wifi44Manager.class);
bindProcessor();
}
#Override
protected void bindProcessor() {
(WifiProcessor.class).to(Wifi44Processor.class);
}
}
#Feature("Wifi")
#CompatibleWithMinOS(OS > 4.4)
#CompatibleWithManufacturer("samsung")
class WifiSamsung44Module extends Wifi44Module {
#Override
protected void bindProcessor() {
bind(WifiProcessor.class).to(SamsungWifiProcessor.class);
}
#Feature("NFC")
public class NfcDefaultModule extends AbstractModule {
#Override
protected void configure() {
bind(NfcManager.class).to(NfcDefaultManager.class);
}
}
#Feature("NFC")
#CompatibleWithMinOS(OS > 6.0)
class Nfc60Module extends NfcDefaultModule {
#Override
protected void configure() {
bind(NfcManager.class).to(Nfc60Manager.class);
}
}
public interface WifiManager {
//bunch of methods to implement
}
public interface WifiProcessor {
//bunch of methods to implement
}
public interface NfcManager {
//bunch of methods to implement
}
public class SuperModule extends AbstractModule {
private final List<Module> chosenModules = new ArrayList<Module>();
public void addModules(List<Module> features) {
chosenModules.addAll(features);
}
#Override
protected void configure() {
for (Module feature: chosenModules) {
feature.configure(binder())
}
}
}
so at startup the app does this:
SuperModule superModule = new SuperModule();
superModule.addModules(crazyBusinessLogic());
Injector injector = Guice.createInjector(Stage.PRODUCTION, superModule);
where crazyBusinessLogic() reads the annotations of all the modules and determines a single one to use for each feature based on device properties. For example:
a Samsung device with OS = 5.0 will have crazyBusinessLogic() return the list { new WifiSamsung44Module(), new NfcDefaultModule() }
a Samsung device with OS = 7.0 will have crazyBusinessLogic() return the list { new WifiSamsung44Module(), new Nfc60Module() }
a Nexus device with OS = 7.0 will have crazyBusinessLogic() return the list { new Wifi44Module(), new Nfc60Module() }
and so on....
Is there any way to do the same with Dagger? Dagger seems to require you to pass the list of modules in the Component annotation.
I read a blog that seems to work on a small demo, but it seems clunky and the extra if statement and extra interfaces for components might cause my code to balloon.
https://blog.davidmedenjak.com/android/2017/04/28/dagger-providing-different-implementations.html
Is there any way to just use a list of modules returned from a function like we are doing in Guice? If not, what would be the closest way that would minimize rewriting the annotations and the crazyBusinessLogic() method?
Dagger generates code at compile-time, so you are not going to have as much module flexibility as you did in Guice; instead of Guice being able to reflectively discover #Provides methods and run a reflective configure() method, Dagger is going to need to know how to create every implementation it may need at runtime, and it's going to need to know that at compile time. Consequently, there's no way to pass an arbitrary array of Modules and have Dagger correctly wire your graph; it defeats the compile-time checking and performance that Dagger was written to provide.
That said, you seem to be okay with a single APK containing all possible implementations, so the only matter is selecting between them at runtime. This is very possible in Dagger, and will probably fall into one of four solutions: David's component-dependencies-based solution, Module subclasses, stateful module instances, or #BindsInstance-based redirection.
Component dependencies
As in David's blog you linked, you can define an interface with a set of bindings that you need to pass in, and then supply those bindings through an implementation of that interface passed into the builder. Though the structure of the interface makes this well-designed to pass Dagger #Component implementations into other Dagger #Component implementations, the interface may be implemented by anything.
However, I'm not sure this solution suits you well: This structure is also best for inheriting freestanding implementations, rather than in your case where your various WifiManager implementations all have dependencies that your graph needs to satisfy. You might be drawn to this type of solution if you need to support a "plugin" architecture, or if your Dagger graph is so huge that a single graph shouldn't contain all of the classes in your app, but unless you have those constraints you may find this solution verbose and restrictive.
Module subclasses
Dagger allows for non-final modules, and allows for the passing of instances into modules, so you can simulate the approach you have by passing subclasses of your modules into the Builder of your Component. Because the ability to substitute/override implementations is frequently associated with testing, this is described on the Dagger 2 Testing page under the heading "Option 1: Override bindings by subclassing modules (don’t do this!)"—it clearly describes the caveats of this approach, notably that the virtual method call will be slower than a static #Provides method, and that any overridden #Provides methods will necessarily need to take all parameters that any implementation uses.
// Your base Module
#Module public class WifiModule {
#Provides WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) {
/* abstract would be better, but abstract methods usually power
* #Binds, #BindsOptionalOf, and other declarative methods, so
* Dagger doesn't allow abstract #Provides methods. */
throw new UnsupportedOperationException();
}
}
// Your Samsung Wifi module
#Module public class SamsungWifiModule {
#Override WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) {
return new SamsungWifiManager(dep1); // Dep2 unused
}
}
// Your Huawei Wifi module
#Module public class HuaweiWifiModule {
#Override WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) {
return new HuaweiWifiManager(dep1, dep2);
}
}
// To create your Component
YourAppComponent component = YourAppComponent.builder()
.baseWifiModule(new SamsungWifiModule()) // or name it anything
// via #Component.Builder
.build();
This works, as you can supply a single Module instance and treat it as an abstract factory pattern, but by calling new unnecessarily, you're not using Dagger to its full potential. Furthermore, the need to maintain a full list of all possible dependencies may make this more trouble than it's worth, especially given that you want all dependencies to ship in the same APK. (This might be a lighter-weight alternative if you need certain kinds of plugin architecture, or you want to avoid shipping an implementation entirely based on compile-time flags or conditions.)
Module instances
The ability to supply a possibly-virtual Module was really meant more for passing module instances with constructor arguments, which you could then use for choosing between implementations.
// Your NFC module
#Module public class NfcModule {
private final boolean useNfc60;
public NfcModule(boolean useNfc60) { this.useNfc60 = useNfc60; }
#Override NfcManager provideNfcManager() {
if (useNfc60) {
return new Nfc60Manager();
}
return new NfcDefaultManager();
}
}
// To create your Component
YourAppComponent component = YourAppComponent.builder()
.nfcModule(new NfcModule(true)) // again, customize with #Component.Builder
.build();
Again, this doesn't use Dagger to its fullest potential; you can do that by manually delegating to the right Provider you want.
// Your NFC module
#Module public class NfcModule {
private final boolean useNfc60;
public NfcModule(boolean useNfc60) { this.useNfc60 = useNfc60; }
#Override NfcManager provideNfcManager(
Provider<Nfc60Manager> nfc60Provider,
Provider<NfcDefaultManager> nfcDefaultProvider) {
if (useNfc60) {
return nfc60Provider.get();
}
return nfcDefaultProvider.get();
}
}
Better! Now you don't create any instances unless you need them, and Nfc60Manager and NfcDefaultManager can take arbitrary parameters that Dagger supplies. This leads to the fourth solution:
Inject the configuration
// Your NFC module
#Module public abstract class NfcModule {
#Provides static NfcManager provideNfcManager(
YourConfiguration yourConfiguration,
Provider<Nfc60Manager> nfc60Provider,
Provider<NfcDefaultManager> nfcDefaultProvider) {
if (yourConfiguration.useNfc60()) {
return nfc60Provider.get();
}
return nfcDefaultProvider.get();
}
}
// To create your Component
YourAppComponent component = YourAppComponent.builder()
// Use #Component.Builder and #BindsInstance to make this easy
.yourConfiguration(getConfigFromBusinessLogic())
.build();
This way you can encapsulate your business logic in your own configuration object, let Dagger provide your required methods, and go back to abstract modules with static #Provides for the best performance. Furthermore, you don't need to use Dagger #Module instances for your API, which hides implementation details and makes it easier to move away from Dagger later if your needs change. For your case, I recommend this solution; it'll take some restructuring, but I think you'll wind up with a clearer structure.
Side note about Guice Module#configure(Binder)
It's not idiomatic to call feature.configure(binder()); please use install(feature); instead. This allows Guice to better describe where errors occur in your code, discover #Provides methods in your Modules, and to de-duplicate your module instances in case a module is installed more than once.
Is there any way to just use a list of modules returned from a
function like we are doing in Guice? If not, what would be the closest
way that would minimize rewriting the annotations and the
crazyBusinessLogic() method?
Not sure this is the answer you're looking for, but just in case you do have other options and for other community members I will describe completely different approach.
I would say that the way you used Guice until now is an abuse of DI framework, and you will be much better off leveraging this opportunity to remove this abuse instead of implementing it in Dagger.
Let me explain.
The main goal of dependency injection architectural pattern is to have construction logic segregated from functional logic.
What you basically want to achieve is standard polymorphism - provide different implementations based on a set of parameters.
If you use Modules and Components for that purpose, you will end up structuring your DI code according to business rules governing the need for these polymorphic implementations.
Not only will this approach requires much more boilerplate, but it also prevents emergence of cohesive Modules that have meaningful structure and provide insights into application's design and architecture.
In addition, I doubt you will be able to unit test these business rules "encoded" inside dependency injection logic.
There are two approaches which are much better IMHO.
First approach is still not very clean, but, at least, it doesn't compromise the large scale structure of dependency injection code:
#Provides
WifiManager wifiManager(DeviceInfoProvider deviceInfoProvider) {
if (deviceInfoProvider.isPostKitKat() ) {
if (deviceInfoProvider.isSamsung()) {
return new WifiMinagerSamsungPostKitKat();
} else {
return new WifiMinagerPostKitKat();
}
} else {
return new WifiMinagerPreKitKat();
}
}
The logic that chooses between implementation still resides in DI code, but, at least, it did not make it into the large scale structure of that part.
But the best solution in this case is to make a proper object oriented design, instead of abusing DI framework.
I'm pretty sure that the source code of all these classes is very similar. They might even inherit from one another while overriding just one single method.
In this case, the right approach is not duplication/inheritance, but composition using Strategy design pattern.
You would extract the "strategy" part into a standalone hierarchy of classes, and define a factory class that constructs them based on system's parameters. Then, you could do it like this:
#Provides
WiFiStrategyFactory wiFiStrategyFactory(DeviceInfoProvider deviceInfoProvider) {
return new WiFiStrategyFactory(deviceInfoProvider);
}
#Provides
WifiManager wifiManager(WiFiStrategyFactory wiFiStrategyFactory) {
return new WifiMinager(WiFiStrategyFactory.newWiFiStrategy());
}
Now construction logic is simple and clear. The differentiation between strategies encapsulated inside WiFiStrategyFactory and can be unit tested.
The best part of this proper approach is that when a new strategy will need to be implemented (because we all know that Android fragmentation is unpredictable), you won't need to implement new Modules and Components, or make any changes to DI structure. This new requirement will be handled by just providing yet another implementation of the strategy and adding the instantiation logic to the factory.
All that while being kept safe with unit tests.

Difference in the use of scopes in Dagger 2

I use Dagger 2 to perform Dependency Inversion rule in my app. I was just looking on Clean Architecture example by Fernando Cejas and I had a question - what is difference between the two approaches presented below:
If I mark the class like so:
#Singleton // or #PerActivity or #PerFragment, nevermind
public class UserDataStoreFactory {
private final Context context;
private final UserCache
}
Or if I create a module, where I define a provide-method and add this module into the any component (PerActivity, PerFragment and so on, nevermind)
#Module
public class SomeModule {
#Provides
#Singleton // or #PerActivity or #PerFragment, nevermind
UserDataStoreFactory providesUserDataStoreFactory (Context context, UserCache userCache) {
return new UserDataStoreFactory(context, userCache)
}
}
The two approaches are the same: Both will allow your UserDataStoreFactory to be injected throughout your application in the Singleton (or #PerActivity/#PerFragment/nevermind) scope.
The former approach, marking the class with the scope, will only work if the class has an #Inject-annotated constructor. The second approach does not require that constructor annotation, but does also require additional boilerplate code that is subject to change when the constructor arguments change. This makes the first approach more resilient to dependency changes on UserDataStoreFactory, even though they both achieve the same end result in your graph; however, the first approach may only be possible if the class is code you can change, or that is otherwise structured for scoped dependency injection.

MVP pattern. Is it a good practice to inject all presenter's dependencies in a constructor of the presenter?

I want my code to be testable and flexible and I cannot make a choice whether I need to pass all dependencies explicitly to a constructor of a presenter or it's better to pass only View interface to a constructor and inject all dependencies in it.
It is a desirable pattern.
Every time you have access to object constructor, you should inject dependencies in constructor or other methods. #Inject annotation is indended mainly to be used inside object, that are not created by you.
When injecting all dependencies in constructor, during tests you pass all your dependencies to your model during initialisation. Therefore every test might contain different dependencies and different instance of created class. That is also the aim of unit tests - provide a sandbox for every test.
It is also easier to mock dependencies with Mockito.
Remember, that in unit tests, you don't have dependency any framework configured. A unit test usually contains created model and nothing more. Everything must be created by you (or mocked by framework)
Here is a sample unit test that proves the statement above:
#RunWith(MockitoJUnitRunner.class)
public class GetAreasUseCaseTest {
#Mock ApiManager mApiManager;
#Mock DatabaseManager mDatabaseManager;
private GetAreasUseCase mGetAreasUseCase;
#Rule
public final RxSchedulersOverrideRule mOverrideSchedulersRule = new RxSchedulersOverrideRule();
#Before
public void setUp() {
mGetAreasUseCase = new GetAreasUseCase(mApiManager,
mDatabaseManager);
doReturn(Observable.empty())
.when(mDatabaseManager)
.insertAreas(any(Area.class));
}
#Test
public void testGetAreasUseCaseApiInteraction() throws Exception {
TestSubscriber<List<Area>> testSubscriber = new TestSubscriber<>();
setCorrectApiResponse();
boolean input = true;
Observable testedObservable = mGetAreasUseCase.build(input);
testedObservable.subscribe(testSubscriber);
verify(mApiManager).getAreas(anyLong());
}
}
As you can see the structure of the test is very clear. It is well known, what is mocked and which object is tested. You have the control of the behaviour of dependencies.
If you plan to do only instrumentation tests, then you are provided with the ApplicationContext in tests and Dagger is properly initialised. There is no difference here. Of course you might still emulate Module's and Component's behaviour and provide custom object's Mocks instead of real classes (Example)
its not a bad practice but its hard to change the code in later changes. what if you want to add a new dependency for presenter ? then you have to change almost everything in your code.
what you are going to do actually is called Dependency Injection and the best practice is to do Dependency Injection, is using libraries like Dagger.
i think Dagger is the most powerful Dependency Injection library up to now. you will write some methods for providing your dependencies and Dagger will provide them for you whenever you want them using a #inject Annotation. for a complete instruction see this link:
http://www.vogella.com/tutorials/Dagger/article.html

Android plain Junit with Dagger 2

I used to work in MVP and I usually test my presenters using a plain Junit (Not the Instrumentation !) , since Presenters only have the business logic and no references to Android internals whatsoever.
Now by switching to Dagger 2 , I understood that I have a problem setting up a "TestModule" for my app component.
Creating a component will not work from within a test class (probably because "apt" is not running there)
Didn't find any examples for using Dagger with a standard Junit testing. Every example I have found only relies on Instrumentation testing or Roboelectric (which basically mocks Activities and other Android related stuff) , but this is just a UI testing for me , and I don't need that.
Just to make things clear , I am talking about the tests that are located at app->src->test folder not the app->src->androidTest !
So do I do something wrong ? Or missing something ? Can anyone explain or give examples on how to use Dagger 2 in normal unit tests ?
I'm not sure if my solution will work for you but I see no reason it shouldn't.
First I created testInjectionComponent
#Singleton
#Component(modules = {MockNetworkModule.class})
public interface MockInjectionComponent extends InjectionComponent {
void inject(DaggerUnitTest daggerUnitTest);
}
Then my Unit Tests I add injection in the before method. like so:
#Before
public void setUp() throws Exception {
MockInjectionComponent mockInjectionComponent = DaggerMockInjectionComponent
.builder()
.mockNetworkModule(new MockNetworkModule())
.build();
mockInjectionComponent.inject(this);
}
Then I just Annotate my Injected Object.
EDIT :
Do not forget to add testApt "com.google.dagger:dagger-compiler:$daggerVersion" at your app.gradle file .
As mentioned by the accepted answer. Do not forget to add :
For Java
Android Test
androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
testAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
For Kotlin
Android Test
kaptAndroidTest 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
kaptTest 'com.google.dagger:dagger-compiler:$dagger_version'
You don't need any dagger to test your presenter. Dagger's job is it to fullfill the dependencies of your classes (dependency injection).
For example you have this Presenter:
public class MyPresenter {
Database database;
ApiService apiService;
#Inject
public MyPresenter(final Database database, final ApiService apiService) {
this.database = database;
this.apiService = apiService;
}
}
Dagger will provide your Presenter with the database and apiService objects for your presenter to use them. When running the actual app (not a test) these will be real objects with real functionality.
When testing the presenter, you want to test only the presenter, everything else should be mocked.
So when you create the presenter in your PresenterTest, you create it with mocked versions of database and apiService.
You can then test how your presenter interacts with these object by
a. mocking the objects behaviour like
when(database.getSomething()).thenReturn(something)
b. verify your presenter does what you want it to do with these objects like
verify(database).saveSomething()
(pseudo code)
Standard way to mock would be Mockito.
You can swap out real modules with fake modules in two ways: do it at compile time using flavors as recommended by google architecture samples or at runtime by creating an abstract method which injects the real thing in production code and fake dependencies in test code. In the second case your test has to subclass the class that you want to mock and build the component from scrach

Injecting test module with dagger2

I use Dagger2 in my android app.
Basically I inject a HttpClient (interface) in MainActivity.
#Module
public class MainActivityModule{
#Provides public HttpClient providesHttpComponent(){
return new RealHttpClient();
}
}
#Component( modules = MainActivityModule.class )
public interface MainActivityComponent {
public MainActivity injectActivity(MainActivity);
}
public class MainActivity extends Activity {
public void onCreate(Bundle saved){
super.onCreate();
injectDependencies();
}
protected void injectDependencies(){
Dagger_MainActivityComponent
.builder()
.mainActivityComponent( new MainActivityModule())
.build()
.injectActivity(this);
}
}
So far so good, that works like expected. Now I want to write some unit tests (not android instrumentation tests) for MainActivity where I want to use TestMainActivityModule instead of MainActivityModule.
#Module (overrides = true )
public class TestMainActivtiyModule extends MainActivityModule {
#Provides public HttpClient(){
return new MockHttpClient();
}
}
My question is: How do I force MainActivity to use TestMainActivitiyModule instead of MainActivityModule? Is there a good solution for that?
My current approach is to use inheritance and to override getModule(), something like this
public class TestMainActivity extend MainActivity {
#Override
protected void injectDependencies(){
Dagger_MainActivityComponent
.builder()
.mainActivityComponent( new TestMainActivtiyModule())
.build()
.injectActivity(this);
}
}
and to run unit test against TestMainActivity instead of MainActivity.
I guess it works, but one of the problems I'm facing with this approach is that I can't start TestMainActivity with an Intent because I can't specify it in AndroidManifest.xml
Does anyone know a better approach for unit testing with dagger2 on android?
The approach I've started using has involved maintaining two modules (one for the app, one for testing) in parallel build variants (ex: app and integration). Still not sure how well that solution scales so YMMV. I'd be very happy to see a better solution!
This is also a great read: http://engineering.circle.com/instrumentation-testing-with-dagger-mockito-and-espresso/
I would really suggest you to check this boilerplate since it is fully based on DI using Dagger2. It also shows how you can replace your dependencies in the test environment in a very neat way.
The dependencies currently handled by the boiler plate are the following:
Database dependency: encapsulates all the database operations.
Shared preferences dependency: deals with shared preferences.
Local files dependency: which deals with saving on files.
Analytics dependency: covers all the operation of reporting events to your analytics backend (GA, Segment, FB, Flurry ..)
Logging dependency: encapsulates all the operations related to logging to your console
Api dependency: encapsulates all the API related operations
The power of dependency injection comes really handy especially for testing since you can easily switch your dependencies in the test environment to dummy dependencies.

Categories

Resources