Say class A depends on B, and that I want to test class A.
I create a test for class A in which I want to mock B.
Class B is injected into class A using Dagger2 (using a Module and a Component). Meaning, class A has a class member:
#Inject
B mB;
In my test class, I create an instance of A in the setUp() method.
How to I provide the mocked instance of the class B to A?
You will need to provide a mock implementation using a mock build flavor if you normally inject your dependency with Dagger. Usually this is done by replacing something like ProdModule with MockModule and then #Provides a mock implementation instead in your mock or test flavor.
Otherwise, a good testing option is Mockito if you want to guarantee that your mock returns what you want it to so that you don't have to worry about the mock implementation having a bug in it.
Related
While running an Espresso test on Android, is there any way to inject dependencies within a custom TestRule?
why would you inject in Testcases? You should use DI when you need to pass instances in constructors... using this approach lets you mock those instances in testcases...
#Inject //Constructor getting injected instance
public classToBeTested(NewInstance instancenew){......}
If you test this class you can provide any mock-object to this class... This should be the way using DI...
My goal is to write instrumentation tests where the class under test is an Activity which has a mock presenter injected into it. I'd also like the presenter to have the same life as the activity that holds it. Finally, the mocks presenter that gets injected in the activity should also be able to be referenced from the test class so that the mock can be setup, verified etc... as part of the test.
Project setup:
I'm using gradle flavors to break the project into source sets that can be used for the different types of tests I'm trying to write. I have a mockRepo flavor where I have Dagger components and modules that create a "real" instance of all of my dependencies except for my repositories which I mock. I have an endToEnd flavor where I inject real instances of every dependency. The last flavor I'm attempting to add is a mockPresenter flavor which creates mock instances of the presenters so that I can truly isolate my views and ensure that when an activity's lifecycle methods run that the correct methods are called on the presenter.
The mockRepo and endToEnd flavors were pretty straightforward to write because in both cases I was able to extend the application class for testing and was able to swap out different dagger components in the application class which made it very easy to inject a mock repository.
The mockPresenter flavor is giving me problems. The issue is that I'm unable to inject a mock presenter into MainActivity without adding a method like
DaggerMainComponent getMainComponent() {
return mDaggerMainComponent
}
to my application class. I'd prefer not to do that because it seems like the application class shouldn't need to know about DaggerMainComponent at all and it's just a workaround.
Here's how the injection code for MainActivity currently looks:
#Inject
MainContract.Presenter mMainPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMainComponent = DaggerMainComponent
.builder()
.appInjector(((MyApplication) getApplicationContext()).getAppComponent())
.mainModule(new MainModule())
.mainPresenterModule(new MainPresenterModule())
.build();
mMainComponent.inject(this);
mMainPresenter.setView(this);
}
What I like about doing things this way is that in onCreate I used the Dagger generated class called DaggerMainComponent to create an instance of component which guarantees that I will get a new Component which guarantees that I will get a new presenter every time onCreate runs. Also, I can get a reference to the component in onCreate and dereference it in onDestroy(). However, it also seems that it isn't possible to inject a mock MainPresenter because the only chance I have to do that is this here:
.mainPresenterModule(new MainPresenterModule())
MainPresenterModule only creates "real" instances of the presenter and if I were so use something like:
.mainPresenterModule(new MockPresenterModule())
then there would be no way to use a "real" version of the presenter for the production version of the app.
The workarounds that I've seen for this situation all seem to involve having a method on the application class that either return a component or a module. In the production version of the app the application class would return either a component that depends on modules that provide real instances or could even just supply the module itself. The test version of the app would have a test application that would extend the production application class and override some of the methods so when the test application was running it would return either the mock version of the component which would declare modules that would supply mocks or it would return the mock modules directly.
The other workaround I've seen uses an #ExposedForTesting annotation in the application class which seems like something I'd like to avoid.
Having the application class expose getter methods so that mock / real dependencies can be injected seems like a code smell to me because I can't see a real reason that the application class should know about the dependencies that should be injected into MainActivity. Also, it seems like you'd end up with one getter method per Activity or Fragment which would start to bloat the application class.
Is there a way to inject mocks into an Activity so that the application class doesn't play such a big part in providing the correct modules or components?
I have some misunderstandings of the way that dagger works:
There are only two ways to satisfy dependency: whether the #Provide method returns the instance, or the class should have #Singleton annotation, is that right? Must the class constructor have #Inject annotation in the latter case?
As I see, the ObjectGraph generates all the injection stuff. And it's said, that its inject(T instance) should be called to inject fields. However, I can just annotate my field with #Inject and it goes (field's class is #Singletone). ObjectGraph is not needed to satisfy such a dependency, right?
What about injects{} in #Module, what specifically does it give? Plz, provide an example of benefit when you keep all the injectable classes list.
Yes, there are two ways: #Provide methods in modules and #Singleton classes with #Inject at constructor.
Must the class constructor have #Inject annotation in the latter case?
Yes, otherwise object wouldn't be created by Dagger.
I don't think #Singleton with field injection could work. Because #Singleton at class with constructor injection means Dagger is responsible to keep one instance of this class. And it can create this class using constructor injection if all dependencies are satisfied. However, #Singleton with field injection seems misuse to me, cause keeping single instance of this class is user's responsibility now. Dagger can't instantiate this object itself.
Are you sure this configuration compiles and runs? And if it is check #Inject fields, they should be null by my understanding.
injects={} in #Module returns set of classes passed to ObjectGraph.inject(T class) or ObjectGraph.get(Class<T> class). Referring documentation:
It is an error to call ObjectGraph.get(java.lang.Class) or ObjectGraph.inject(T) with a type that isn't listed in the injects set for any of the object graph's modules. Making such a call will trigger an IllegalArgumentException at runtime.
This set helps Dagger to perform static analysis to detects errors and unsatisfied dependencies.
You can find some examples in this thread.
Dagger is kicking my butt.
I have the following layout.
A MainActivity which is injected into the Object graph.
The MainActivity #Injects a MainPresenter interface. This is Provided via the MainModule which returns a concrete implementation.
The concrete MainPresenter implementation takes a FileContentInteractor interface. This is provided in the InteractorModule which returns a concrete FileContentInteractor implementation.
MainModule can do this because it includes InteractorModule.class
Up until now everything is great. From this point on is where it gets tricky.
The concrete FileContentInteractor implementation injects some member variables using #Inject. These member variables are all interfaces which are provided concrete implementations via their respective modules.
An example is #Inject ThreadExecutor threadExecutor. I thought that this would be provided because;
InteractorModule includes ExecutorModule.class.
ExecutorModule.class #Provides a concrete implementation of ThreadExecutor in the form of a TaasExecutor object.
TaskExecutor object has no injected dependencies.
When I run my app and the MainActivity opens the following happens;
MainActivity calls a method of MainPresenter implementation - works fine!
MainPresenter has a FileContentInteractor implementation and calls a method on it - works
The method in FileContentInteractor implementation tries to call a method in ThreadExecutor implementation (which is #Injected as a member variable). This failed because the #Injected ThreadExecutor implementation is null.
Can anyone help?
Ok so it turns out I was kicking my own butt...
I was referencing the concrete classes with my #Inject members but using the Interfaces as my #Provides.
Amending the #Inject members from classes to interfaces fixed it.
I'm using AndroidAnnotations in my project and just hooked Dagger + Robolectric + Espresso in.
Currently I have an Activity with SharedPrefences and a class 'injected' by AndroidAnnotations.
public class SomeActivity extends Activity {
#Pref Prefs_ prefs;
#Bean SomeManager mManager;
}
Now if I want to use a Dagger to inject these two, what my #Provide methods should look like?
Thank you very much.
Your #Provide methods should call the getInstance_ method of generated bean classes. This methods creates a new instance and injects the necessary objects. For the generated preference, you should call the constructor new Prefs_(context). However, this is not a too clean way since these generated methods are supposed to called by AA. Also Dagger integration is only working in the snapshot release, check out this thread. But as you can see, you can use Dagger now, but it is not convenient, and it seems to be better to just use AA injection for the AA generated classes for now.