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...
Related
So I'm having to write unit tests and I need to test my ViewModels.
I have a base ViewModel and that BaseViewModel extends AndroidViewModel. The reason I extend AndroidViewModel is so that I can use the context for Dagger.
My BaseViewModel.
public class BaseViewModel extends AndroidViewModel {
protected #Inject SharedPreferencesHelper sharedPreferencesHelper;
public BaseViewModel(#NonNull Application application) {
super(application);
//Only inject sharedPreferences since it is used in almost all of the VMs.
((CommissioningApplication) getApplication()).getAppComponent().inject(this);
}
}
Essentially I'm having issues creating an instance of my ViewModel in my unit tests. I have tried many things and have been unsuccessful.
I have considered extending ViewModel instead of AndroidViewModel and creating a Factory that will pass the application context to my ViewModels. But in the end I will run towards the same issue whenever I try to create an instance of my ViewModel.
Does anyone have an example that I could follow on how to test this? Or would have I have to do Instrumented testing instead of Unit testing?
I'm very new to testing so maybe I'm not doing things properly, any help would be great.
Thanks
As stated here:
Mockito.mock(Application::class.java)
I'm starting to think in mvp, dagger should not be used in the presenter. The usual way to construct dagger is using a global component and have subcomponents for scoping the graph. This global component will take an applicationContext as a parameter on creating the appmodule.java class usually. Having the application context given makes life easier.
That's all fine but if I use a module from the global component or even a subcomponent, the context should be passed in. So that means if I inject the presenter with dagger it will be tied to the applicationContext. THIS MAKES IT DIFFICULT TO TEST as junit. Android code should not be in presenter.
So I'm asking is the best practice to just use dagger in activities fragments broadcast receivers and services only? Speaking in terms of mvp architecture that is. The other solution could be to design another dagger component but not a subcomponent that is not related to appcomponent or an applicationContext. What is the common practice?
UPDATE:
LETS look at a real example:
usually we'd create a appComponent that's close in application override like this:
public class MyApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = initDagger(this);
}
public AppComponent getAppComponent() {
return appComponent;
}
protected AppComponent initDagger(PomeloApplication application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}}
and then for example in the presenter we'd do this in the constructor:
public WelcomePresenter(Context context) {
super(context);
presenterComponent = ((MyApplication) context).getAppComponent();
presenterComponent.inject(this);
}
so because I wanted the application Context for dagger providers I ended up forcing callers to depend on a context Object. Basically, you cant get the AppComponent modules until you get a context so you can cast it as "MyApplication" and inject from the component? So i was thinking why is this dagger pattern making me force the presenter to have a context?
Even if we used a subComponent it would still depend on the AppComponent and then in our test case we'd have to deal with an application context being created to get dagger to inject.
So I think there should be a rule - in MVP prefer manual constructor injection over dagger injection
Because class shouldn’t have knowledge about how it is injected. Something being injected should not care about how it is injected.
dagger.android outlines the problem i am talking about as per the accepted answer.
I also agree with the statement "Not having android related code in your presenter is always a good idea, and dagger does not interfere with that."
First, what I want to draw attention that starting from dagger version 2.10 you can use dagger.android package which simplifies injection in the activities and fragments, so you don't need to use MyApplication casting across the app.
Second, why are you not injecting WelcomePresenter into activity or fragment but vice versa?
Dependency injection is useful for Android components (activities, etc.) primarily because these classes must have a no-arg constructor so the framework can construct instances of them. Any class that isn't required to have a no-arg constructor should simply receive all its dependencies in its constructor.
Edit: A side note about how you're getting a reference to the AppComponent.
Application is-a Context, and the Context returned by Context#getApplicationContext() is usually the Application instance. But this is not necessarily the case. The one place I know of where it may not be the case is in an emulator. So this cast can fail:
presenterComponent = ((MyApplication) context).getAppComponent();
Mutable static fields are evil in general, and even more so on Android. But IMO, the one place you can get away with it is in your Application class. There is only one instance of Application, and no other component will ever exist before Application#onCreate() is called. So it's acceptable to make your AppComponent a static field of MyApplication, initialize it in onCreate(), and provide a static getter, as long as you only call the getter from the lifecycle methods of other components.
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?
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.
How can I create a singleton that gets injected with an application context. Just annotating with #Singleton and then using #Inject on the constructor ends up generating an UnscopedProvider class that will not compile. How can I make an ApplicationScoped provider... or is there another mechanism to accomplish this?
The solution I ended on here is to create an init(Application app) {} method on the Singleton, then inject the Singleton into my #Application annotated class and call the init method using the Application that's injected into that class. I think this is the only way to do this currently.