I'm currently developing an Android MVP Application, and I'm trying to separate my dependencies in different Dagger2 Modules.
The problem I'm having is about changing a module in Unit Test Time. The scenario is the following:
LoginComponent, which uses two modules: LoginModule and HTTPModule
LoginModule in one of its methods requires an OkHttp instance, which is provided by HTTPModule.
The code is the following:
#Singleton
#Component(modules = {LoginModule.class, HTTPModule.class})
public interface LoginComponent {
}
#Module(includes = {HTTPModule.class})
public class LoginModule {
#Provides
#Singleton
public MyThing provideMyThing(OkHttpClient client) {
// Do things with it
}
}
#Module
public class HTTPModule {
#Provides
#Singleton
public OkHttpClient provideOkHttpClient(){
// Return the OkHttpClient
}
}
The thing is, at test time I would need to change the OkHttpClient that is returned (by making it accept all the certificates, as when I run it on the JVM it does not accept the LetsEncrypt certificate).
Also I would need that because I need to declare that MyTest.class can be injected with module, and as MyTest.class is under the app/src/test/ folder, it's not visible for the classes that are placed under app/src/main/. What I've done until now is to copy and paste the Component and the modules to the /test/ folder, and make the injected class declaration there. But I know there must be a proper way to achieve what I'm looking for.
Another thing I've tried is annotating the methods with custom Scopes (creating a #TestScope annotation). However this leads me to the same problem that I had commented before: I cannot make the MyTest.class visible to the component, because it's placed under the /test/ folder.
I've already checked other similar questions, such as this one and this another one, but this last one is for running tests with Robolectric, and by now I'm able to unit test most of my code with JUnit4 only (Android Studio 2-Beta 8).
If anyone could point me to the right direction, I would be more than grateful.
Thanks in advance!
You're using Dependency injection in a way that still keeps your code tightly coupled. Generally speaking you want your dependencies to be interfaces instead of actual classes. This keeps your code nice and loose, easy to read, modify and maintain.
Hide your network operations behind an interface to allow you to modify the network implementation whenever you need to. This could be done for testing - in your case, but it will also allow you to switch out the network library if you'll want to or need to in the future without changing any other code.
Try something like this:
#Module
public class HTTPModule {
#Provides
#Singleton
public NetworkProvider provideNetworkProvider(){
// Return the Network provider
}
}
The network abstraction layer:
public interface NetworkProvider {
// Methods to send requests and receive async responses
}
The OkHttp implementation:
public class OkHttpNetworkProvider implements NetworkProvider {
// Implement NetworkProvider. This is the only class that
// knows about OkHttp and its components
}
Now you can create a mock version of NetworkProvider and use it for testing, whether via a test module or directly.
Related
So I am currently in the process of learning dagger 2, and from the tutorials that I've read so far, for a dependency to be injected, the #Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work, or if I don't want other parts of the code to know that dagger exists.
The application structure I have at the moment is:
App Module - where I'd like to put my DI code in (e.g. dagger modules, etc).
Presentation Module - Views/ViewModels etc.
Domain Module - Use Cases etc.
Data Module - Repositories etc.
With pretty much this style of classes contained in my application:
class ExampleViewModelImpl(useCase: ExampleUseCase): ViewModel() in Presentation (gets initialised from an Activity or similar).
class ExampleUseCaseImpl(repository: ExampleRepository): ExampleUseCase in Domain
class ExampleRepositoryImpl(dao: ExampleDao): ExampleRepository in Data
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible? Code examples of how this is achieved would be great.
I am unsure of some terminologies, and wasn't able to find a solution online. If there are good resources which explains what I'm asking, that would also be great.
if I don't want other parts of the code to know that dagger exists.
#Inject is a standard (JSR 330) which Dagger implements. Adding those annotations doesn't have anything to do with Dagger and can be used the same way with other DI frameworks. If it's your code you should just add those #Inject annotations where appropriate. Think of them as documentation: What constructor/field/method must be injected to create & use this object?
The only place where your classes will know that Dagger exists is at the same place where you'd be creating the objects otherwise, too.
Going down that path, of course you could use Dagger without any #Inject annotations, but you'd be writing a lot of unnecessary boilerplate and missing out on the most powerful feature of Dagger at the same time (code generation).
#Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work
That's what #BindsInstance with the #Component.Builder is for (add an object to the component) and what #Provides annotated methods are for (create and initialize an object from a module)
If you really want to write code without #Inject, then you'd do exactly this for all of your objects. This means a lot of modules, and even more #Provides annotated methods. It will work, but I don't see the point in writing all those methods if a single #Inject on the constructor has the same effect.
In my opinion the best thing about Dagger is that I can add / remove / change constructor parameters and don't have to touch any other parts of my code since Dagger will generate new code with the new arguments. In your case you'd have to also change the parameters to the #Provides method as well as the constructor invocation.
Next let's look at how to remove #Inject from fields. Basically you don't want to do field injection, so instead of writing an injection method in the component, you'd write provision methods.
#Component
class MyComponent {
fun inject(activity: MyActivity)
}
class MyActivity {
#Inject lateinit var myDep: Dependency
fun onCreate() {
component.inject(this)
}
}
Removing the #Inject we need to use the provision methods instead.
#Component
class MyComponent {
fun provisionMyDependency() : Dependency
}
class MyActivity {
lateinit var myDep: Dependency
fun onCreate() {
myDep = component.provisionMyDependency()
}
}
It will work and everything, but again, you will miss out on the single best feature of Dagger: Code generation. The example above looks alright because I only added a single dependency, but think about what happens to those 2 different implementations when you add / remove / change dependencies, how well it will scale. If you prefer to do things manually any refactoring will become arduous.
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible?
Your question (especially the title) is in direct conflict with your goal. If you don't want to use those annotations, then you can't use Dagger code generation & injection but have to resort to do it manually as highlighted above.
with as much automated dependency injection as possible
To best utilize Dagger you add #Inject on the constructor and/or fields of every class that should end up on your dependency graph and let Dagger do its thing.
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.
In my app i have an ApplicationScope which provides me with stuff like context and shared prefs.
2 sub components, LoggedInComponent and LoggedOutComponent.
Those 2 subcomponents have other subcomponents which are less relevant to the issue.
I have a NetworkModule which creates my retrofit instance.
Both subcomponents need the NetworkModule but the retrofit might change during the login because the base url and other settings might change.
I was wondering which approach it is better to take:
1. Give both LoggedIn and LoggedOut sub components and the NetworkModule the same scope so both sub components can use this module. Feels a bit hacky and wrong usage of scopes.
2. Put network module in the AppComponent with ApplicationScope. Also wrong usage of scope because network module is recreated so it cannot have same scope and also each recreation will cause the AppComponent and its subcomponents to be recreated.
What would you do? maybe there is a better solution?
You don't want LoggedInComponent and LoggedOutComponent to have differing network behavior, and you don't necessarily need network behavior to be consistent across the lifetime of LoggedInComponent or LoggedOutComponent, so it doesn't make sense for you to have them bind NetworkModule separately. I think it makes sense to make them available through AppComponent. Remember that not everything in AppComponent needs to have ApplicationScope: You can make unscoped bindings in AppComponent, which instructs Dagger to re-fetch or re-create the requested object every time.
Specifically, I would bind NetworkModule into a subcomponent of AppComponent, which you can recreate every time the NetworkModule configuration changes. Not only would this let you encapsulate some of the network details (see "subcomponents for encapsulation" in the Dagger User's Guide), but it would also let you take advantage of dependency injection throughout your network bindings if that's what you're looking for.
To ensure that you're getting the correct current NetworkComponent, you could create a NetworkManager that is singleton-bound (ApplicationScope). You can use that to hold the latest NetworkComponent.
#Module(subcomponents={NetworkComponent.class})
public abstract class ApplicationModule {
/** Unscoped. Fetch this fresh from NetworkComponentHolder each time. */
#Provides Retrofit provideRetrofit(NetworkManager manager) {
return manager.getNetworkComponent().getRetrofit();
}
}
#ApplicationScope public class NetworkManager {
private final Provider<NetworkComponent.Builder> builderProvider;
private NetworkComponent currentComponent;
#Inject public NetworkComponentHolder(
Provider<NetworkComponent.Builder> builderProvider) {
this.builderProvider = builderProvider;
currentComponent = builderProvider.get()
.withNetworkModule(getDefault())
.build();
}
public void updateSettings(String baseUrl) {
currentComponent = builderProvider.get()
.withNetworkModule(new NetworkModule(baseUrl))
.build();
}
public NetworkComponent getNetworkComponent() {
return currentComponent;
}
}
With this, most of your code in AppComponent, LoggedInComponent, and LoggedOutComponent can just inject a Retrofit (or Provider<Retrofit>) whenever they need to make a request. When a response comes back that tells you to update your base URL, you can inject a NetworkManager, call updateSettings, and suddenly new requests for Retrofit will return your new instance. (Note however that old instances to Retrofit may still stick around, but you'll have that problem any time you're changing a dependency belonging to an existing instance.)
p.s. If NetworkModule is lightweight enough, or has consistent-enough bindings, you might opt to put NetworkModule directly onto ApplicationComponent and simply have NetworkManager hold the current Retrofit instance etc. You'll have to make that judgment call based on the number of bindings you want to pass through as provideRetrofit does, compared to the number of bindings you'd want to encapsulate or hide away in a subcomponent.
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
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.