Dagger2 getting instance of activity presenter in fragment presenter - android

I'm really lost and hope you can help me. I'm programming app using MVP and dagger2. One of activities (let's say A ) contains fragments ( B ) in FragmentStatePagerAdapter. Both activity and fragments have their own presenters. I'm handling input in fragments ( B ), and transfer data from it "up" to main presenter ( B -> A ) (of activity holding fragments) and there ( A ) I'm handling network connection.
For this I need instance of main presenter ( A ) in fragment presenter ( B ). I've tried to use dependency on fragment graph but instead of getting already existing instance of presenter it's creating new one with every init. Any advises how I could get something similar to singleton but using ActivityScope?
A graph:
#ActivityScope
#Component(modules = GiftListModule.class, dependencies = AppGraph.class)
public interface GiftListGraph extends AppGraph{
void inject(GiftListActivity giftListActivity);
GiftListPresenter getGiftListPresenter();
final class Initializer {
public static GiftListGraph init(AppGraph appGraph, GiftListView giftListView) {
return DaggerGiftListGraph.builder()
.giftListModule(new GiftListModule(giftListView))
.appGraph(appGraph)
.build();
}
}
}
A module:
#Module
public class GiftListModule {
private final GiftListView giftListView;
public GiftListModule(GiftListView giftListView) {
this.giftListView = giftListView;
}
#Provides GiftListView provideGiftListView() {
return giftListView;
}
#Provides GiftListPresenter provideGiftListPresenter(GiftListView giftListView) {
return new GiftListPresenterImpl(giftListView);
}
}
B graph:
#FragmentScope
#Component(modules = GiftDetailsModule.class, dependencies = GiftListGraph.class)
public interface GiftDetailsGraph {
void inject(GiftDetailsFragment giftDetailsFragment);
GiftDetailsPresenter getGiftDetailsPresenter();
final class Initializer {
public static GiftDetailsGraph init(GiftListGraph giftListGraph, GiftDetailsView giftDetailsView) {
return DaggerGiftDetailsGraph.builder()
.giftDetailsModule(new GiftDetailsModule(giftDetailsView))
.giftListGraph(giftListGraph)
.build();
}
}
}
B module:
#Module
public class GiftDetailsModule {
private final GiftDetailsView giftDetailsView;
public GiftDetailsModule(GiftDetailsView giftDetailsView) {
this.giftDetailsView = giftDetailsView;
}
#Provides GiftDetailsView provideGiftDetailsView() {
return giftDetailsView;
}
#Provides GiftDetailsPresenter provideGiftDetailsPresenter(GiftDetailsView giftDetailsView,
GiftListPresenter giftListPresenter) {
return new GiftDetailsPresenterImpl(giftDetailsView, giftListPresenter);
}
}
Main App:
public class MainApp extends Application {
private static MainApp sInstance;
protected AppGraph appGraph;
protected GiftListGraph giftListGraph;
#Override
public void onCreate() {
super.onCreate();
sInstance = this;
appGraph = AppGraph.Initializer.init(this);
}
public static MainApp getInstance() {
return sInstance;
}
...
public GiftListGraph getGiftListGraph(GiftListView view) {
return giftListGraph = GiftListGraph.Initializer.init(appGraph, view);
}
public GiftDetailsGraph getGiftDetailsGraph(GiftDetailsView view) {
return GiftDetailsGraph.Initializer.init(giftListGraph, view);
}
...
}
Thanks for any help you can give me :)

From DI perspective you are seeing the correct behavior. When you are calling get list graph or detail graph, you are building entirely new graph. (See. new Module calls in your initializer calls). Hence you are getting new instance each time.
There are few options I would consider.
One:
Have a callback interface defined at the activity scope. Have activity implement it. In the process of creating fragment graphs pass the callback impl instance (activity or some impl class) as argument. Your fragment a/b presenter can use that as a dependency. Now both fragment presenter gets a call back.
Two:
Use event bus or broadcast receivers that run at app scope or activity scope. Use that to post message back and forth.
Hope this gives you some ideas.

Related

How to test screens which requires authorization? Espresso + MockWebServer

I am creating UI tests. In order not to interact with the real server, I use MockWebServer. My goal is to emulate various server responses and see how the program as a whole will respond to them. At the moment, I don’t understand how to open screens that require authorization. Of course, I can write a code that will login to the authorization screen, and then go to the desired window. But this requires additional time to complete the test, and I would like to avoid this. I would not want to mocking classes, because I need to check the production version of the application. How can i do this?
For DI, I use Dagger-2. Here is the component code:
#Singleton
#Component(modules = {
AvatarsModule.class,
EncryptionModule.class,
ApiModule.class,
WalletsModule.class,
GeneralModule.class,
InteractorsModule.class,
PushNotificationsModule.class,
AppModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder context(Context context);
AppComponent build();
}
void inject(App app);
}
Here is the class code in which the authorization state is stored:
public class ApiWrapper {
private Api api;
private KeyPair keyPair;
private Account account;
...
public Flowable<Authorization> authorize(KeyPair tempKeyPair) {
return api
.authorize(tempKeyPair.getPublicKeyString().toLowerCase())
.subscribeOn(Schedulers.io())
.doOnNext((authorization -> {
this.account = authorization.getAccount();
this.keyPair = tempKeyPair;
}));
}
...
}
If anyone is still interested. I wrote an InstrumentationTestFacade class in which I put an ApiWrapper object using Dagger. Next, the InstrumentationTestFacade is injected into the Application object. Since the application object is not a singleton, there is no leak of responsibility in the main code, but from the test code you can access this facade using the following code:
Application application = (Application) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
InstrumentationTestFacade facade = application.getInstrumentationTestFacade();
Below is an example:
public class InstrumentationTestFacade {
private LogoutInteractor logoutInteractor;
private SecurityInteractor securityInteractor;
private ApiWrapper apiWrapper;
public InstrumentationTestFacade(
LogoutInteractor logoutInteractor,
SecurityInteractor securityInteractor,
ApiWrapper apiWrapper
) {
this.logoutInteractor = logoutInteractor;
this.securityInteractor = securityInteractor;
this.apiWrapper = apiWrapper;
}
public void logout() {
logoutInteractor.logout();
}
public ApiWrapper getApiWrapper() {
return apiWrapper;
}
public SecurityInteractor getSecurityInteractor() {
return this.securityInteractor;
}
}
public class Application extends MultiDexApplication implements HasActivityInjector, HasServiceInjector {
...
#Inject
InstrumentationTestFacade instrumentationTestFacade;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.context(this)
.build()
.inject(this);
}
...
public InstrumentationTestFacade getInstrumentationTestFacade() {
return instrumentationTestFacade;
}
}

Android Dagger 2 with MVP

Using Dagger 2 for the first time with MVP.
I am stuck at a very simple implementation.
my presenter module takes View Interface in constructor along with context and data manager,I am confused in how to send activity context to the constructor for the view interface..
Any help will be highly appreciated..
Here is my code for App class:
public class App extends Application {
private static App app;
public SampleComponent getSc() {
return sc;
}
private SampleComponent sc;
public static App getApp() {
return app;
}
#Override
public void onCreate() {
super.onCreate();
app = this;
sc = DaggerSampleComponent.builder()
//.sampleModule(new SampleModule())
.presenterModule(new PresenterModule(new MainActivity(), getApplicationContext(), new ModelManager()))
.build();
}
}
Code for Presenter Module :
#Module
public class PresenterModule {
ShowCountContract.view v;
ModelManager mm;
Context c;
public PresenterModule(MainActivity m, Context c,
ModelManager mm) {
this.c = c;
this.mm = mm;
this.v = m;
}
#Singleton
#Provides
PresenterClass getPresentationClass() {
return new PresenterClass(mm, v);
}
}
To handle the Android context the best approach is to create an Application Component with an Application Module. This module should be responsible to provide objects that are common in the entire application, as the Context. And based on that component you can create subcomponents for each feature/activity/etc.
#Module
public class ApplicationModule {
private final Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
Context provideContext() {
return application;
}
}
If you choose to work with just one component (what I do not recommend), your code for DaggerComponent creation will look like this:
DaggerSampleComponent.builder()
.applicationModule(new ApplicationModule(this))
.otherModule(new OtherModule())
.build();
Or you can use Component.Builder
As the Activity instance is created by the Android Framework, we cannot pass the View interface as a constructor parameter. The common way is to create such a method as attachView(ViewInterface) in your Presenter to be able to set an internal property.
Another thing you should change is to remove the Presenter's constructor from App and let the OtherModule be responsible for that:
#Module
public class OtherModule {
#Singleton
#Provides
PresenterClass getPresentationClass(Context ctx) {
return new PresenterClass(ctx, new ModelManager());
}
}
I recommend you to check this article where it goes deeper on Dagger explanation and even shows another Dagger's version that is directly thought to the Android environment.

How can I replace Activity scoped dependencies with mocks using Dagger2

I have a scoped dependency in my Activity and I want to test that activity with some mocks. I have read about different approach that suggest to replace Application component with a test component during the test, but what I want is to replace the Activity component.
For example, I want to test the Activity against mock presenter in my MVP setup.
I believe that replacing component by calling setComponent() on Activity will not work, because Activity dependencies already injected via field injection, so during the test, real object will be used.
How can I resolve this issue? What about Dagger1? Is it has the same issue?
Injecting the Component
First, you create a static class to act as a factory for your Activity. Mine looks a little like this:
public class ActivityComponentFactory {
private static ActivityComponentFactory sInstance;
public static ActivityComponentFactory getInstance() {
if(sInstance == null) sInstance = new ActivityComponentFactory();
return sInstance;
}
#VisibleForTesting
public static void setInstance(ActivityComponentFactory instance) {
sInstance = instance;
}
private ActivityComponentFactory() {
// Singleton
}
public ActivityComponent createActivityComponent() {
return DaggerActivityComponent.create();
}
}
Then just do ActivityComponentFactory.getInstance().createActivityComponent().inject(this); inside your Activities.
For testing, you can replace the factory in your method, before the Activity is created.
Providing mocks
As #EpicPandaForce's answer makes clear, doing this the officially-supported way currently involves a lot of boilerplate and copy/pasted code. The Dagger 2 team need to provide a simpler way of partially overriding Modules.
Until they do though, here's my unnoficial way: Just extend the module.
Let's say you want to replace your ListViewPresenter with a mock. Say you have a PresenterModule which looks like this:
#Module #ActivityScope
public class PresenterModule {
#ActivityScope
public ListViewPresenter provideListViewPresenter() {
return new ListViewPresenter();
}
#ActivityScope
public SomeOtherPresenter provideSomeOtherPresenter() {
return new SomeOtherPresenter();
}
}
You can just do this in your test setup:
ActivityComponentFactory.setInstance(new ActivityComponentFactory() {
#Override
public ActivityComponent createActivityComponent() {
return DaggerActivityComponent.builder()
.presenterModule(new PresenterModule() {
#Override
public ListViewPresenter provideListViewPresenter() {
// Note you don't have to use Mockito, it's just what I use
return Mockito.mock(ListViewPresenter.class);
}
})
.build();
}
});
...and it just works!
Note that you don't have to include the #Provides annotation on the #Override method. In fact, if you do then the Dagger 2 code generation will fail.
This works because the Modules are just simple factories - the generated Component classes take care of caching instances of scoped instances. The #Scope annotations are used by the code generator, but are irrelevant at runtime.
You cannot override modules in Dagger2 [EDIT: you can, just don't specify the #Provides annotation on the mock), which would obviously be the proper solution: just use the builder().somethingModule(new MockSomethingModule()).build() and be done with it!
If you thought mocking is not possible, then I would have seen two possible solutions to this problem. You can either use the modules to contain a pluggable "provider" that can have its implementation changed (I don't favor this because it's just too verbose!)
public interface SomethingProvider {
Something something(Context context);
}
#Module
public class SomethingModule {
private SomethingProvider somethingProvider;
public SomethingModule(SomethingProvider somethingProvider) {
this.somethingProvider = somethingProvider;
}
#Provides
#Singleton
public Something something(Context context) {
return somethingProvider.something(context);
}
}
public class ProdSomethingProvider implements SomethingProvider {
public Something something(Context context) {
return new SomethingImpl(context);
}
}
public class TestSomethingProvider implements SomethingProvider {
public Something something(Context context) {
return new MockSomethingImpl(context);
}
}
SomethingComponent somethingComponent = DaggerSomethingComponent.builder()
.somethingModule(new SomethingModule(new ProdSomethingProvider()))
.build();
Or you can bring the provided classes and injection targets out into their own "metacomponent" interface, which your ApplicationComponent and your TestApplicationComponent extend from.
public interface MetaApplicationComponent {
Something something();
void inject(MainActivity mainActivity);
}
#Component(modules={SomethingModule.class})
#Singleton
public interface ApplicationComponent extends MetaApplicationComponent {
}
#Component(modules={MockSomethingModule.class})
#Singleton
public interface MockApplicationComponent extends MetaApplicationComponent {
}
The third solution is to just extend the modules like in #vaughandroid 's answer. Refer to that, that is the proper way of doing it.
As for activity scoped components... same thing as I mentioned here, it's just a different scope, really.
I've found the following post that solves the problem:
http://blog.sqisland.com/2015/04/dagger-2-espresso-2-mockito.html
You need first to allow to modify the component of the activity:
#Override public void onCreate() {
super.onCreate();
if (component == null) {
component = DaggerDemoApplication_ApplicationComponent
.builder()
.clockModule(new ClockModule())
.build();
}
}
public void setComponent(DemoComponent component) {
this.component = component;
}
public DemoComponent component() {
return component;
}
And modify it in the test case
#Before
public void setUp() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
DemoApplication app
= (DemoApplication) instrumentation.getTargetContext().getApplicationContext();
TestComponent component = DaggerMainActivityTest_TestComponent.builder()
.mockClockModule(new MockClockModule())
.build();
app.setComponent(component);
component.inject(this);
}

In Dagger are Singletons within the sub-graph cached or will they always be recreated when a new activity sub-graph is constructed?

I'm using Dagger to create activity specific object graphs. Within this subgraph, I make use of a Singleton MyPresentationModel.
When i exit my activity, and enter the activity again, my expectation is that a new instance of the activity specific object graph is created, which in turn would create a new instance of Singleton MyPresentationModel (by virtue of the #Singleton semantic per Dagger. See this So answer for specifics) which would then last for the life of the activity specific object graph.
However, this is not what i'm observing, every time the activity specific object graph is created, the same instance of MyPresentationModel is used. I added a debug point into the constructor of MyPresentationModel. The very first time we enter the constructor. Subsequently even on activity exits and reentries, we don't enter the constructor (and because of this the UserSession being used within my Presentation model uses the old value from the very first constructor injection).
While i can technically solve the problem by re-setting UserSession inside MyPresentaitonModel with an external public setter, I want to understand better the mechanics of the activity specific object graph creation/destruction.
By nullifying the graph in my onDestroy, does that still mean that there is a possibility of the Singletons within my subgraph being reused at a later point ? (possibly until they are truly GCed?)
Here's some code:
// MyAppModule
#Module(
includes = { UserSession.class},
injects = { MyApplication.class })
public class MyAppModule {
private final MyApplication _app;
MyAppModule(MyApplication app) {
_app = app;
}
// ...
}
// Main Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_activityObjectGraph = MyApplication.get()
.getObjectGraph()
.plus(Arrays.<Object>asList(new SubModule()).toArray());
// Inject ourselves so subclasses will have dependencies fulfilled when this method returns.
_activityObjectGraph.inject(this);
}
#Override
protected void onDestroy() {
_activityObjectGraph = null;
// this eagerly allows GC, but doesn't necessarily destroy the subgraph ?
super.onDestroy();
}
// SubModule
#Module(injects = { MyPresentationModel.class, MainActivity.class },
addsTo = MyAppModule.class,
library = true)
public class SubModule {}
}
// MyPresentationModel
#Singleton
public class MyPresentationModel {
private UserSession _session;
#Inject
public MyPresentationModel(UserSession session) {
_session = session;
}
public void someMethodThatUsesSessionInfo() {
// _session.getUser() ...
}
}
#weefbellington posted a very informative answer, but reading it made me realize my question was not specific and clear enough. Here's attempt 2:
MyAppModule (main graph) -> provides a Singleton UserSession
MySubModule (sub graph plused onto MyAppModule) -> provides "activity specific" Singleton MyPresentationModel which requires a UserSession (provided my MyAppModule) on construction.
I now close the activity, destroying MySubModule (and also hopefully MyPresentationModel which is a Singleton), I update UserSession with some new information.
I open MainActivity again, thus re-creating the sub-graph from MySubModule, which inturn provides a MyPresentationModel.
The issue I'm noticing is that MyPresentationModel which is the local Singleton is not being reconstructed again i.e. this part of the code:
#Inject
public MyPresentationModel(UserSession session) {
_session = session;
}
is only ever being called once. My expectation was that this part of the code would be run again, and the UserSession would be pulled again from the Main graph and since it was updated, it would hold the updated values. My question is: are Singletons within the sub-graph cached in anyway or will they always be recreated when a new activity sub-graph is spawned?
How MyPresentationModule is injected depends on how your modules are specified. For example, assume that you are injecting the class Foo:
public class Foo {
private final MyPresentationModel model;
#Inject
public Foo(MyPresentationModel model) {
this.model = model;
}
}
If your modules are structured like (A), then the MyPresentationModel singleton will be injected into Foo by the main object graph:
EXAMPLE A
#Module(injects = { Foo.class })
public class MainModule { ... }
#Module(addsTo = MainModule.class, injects = { MyPresentationModel.class })
public class SubModule { ...}
Alternatively, if your modules are structured like (B), then the MyPresentationModel singleton will be injected into Foo by the subgraph:
EXAMPLE B
#Module
public class MainModule { ... }
#Module(addsTo = MainModule.class, injects = { Foo.class, MyPresentationModel.class })
public class SubModule { ... }
In your particular case, since you have specified that MyAppModule injects MyApplication, I would guess that you are trying to inject MyPresentationModel into your Application class. This is probably not what you want to do. You probably want inject this into your Activity class using the submodule, as in (C).
EXAMPLE C
#Module(injects = { MainActivity.class, MyPresentationModel.class },
addsTo = MyAppModule.class,
library = true)
public class SubModule { ... }
public class MainActivity {
#Inject MyPresentationModel presentationModel;
...
}
If you do this the MyPresentationModel singleton will be bound to the Activity subgraph instead of the main graph, and should be disposed when the Activity is destroyed.
Once you have a handle on Dagger, you might want to check out Mortar, which gives you finer-grained control over creation and destruction of ObjectGraph subscopes.

Android functional testing with Dagger

I’m trying to test an Activity with Mockito & Dagger. I have been able to inject dependencies to Activity in my application but when testing the Activity, I have not been able to inject mock to the Activity. Should I inject Activity to test or let getActivity() create it?
public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity> {
#Inject Engine engineMock;
private MainActivity mActivity;
private Button mLogoutBtn;
public MainActivityTest() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
// Inject engineMock to test
ObjectGraph.create(new TestModule()).inject(this);
}
#Override
protected void tearDown() {
if (mActivity != null)
mActivity.finish();
}
#Module(
includes = MainModule.class,
entryPoints = MainActivityTest.class,
overrides = true
)
static class TestModule {
#Provides
#Singleton
Engine provideEngine() {
return mock(Engine.class);
}
}
#UiThreadTest
public void testLogoutButton() {
when(engineMock.isLoggedIn()).thenReturn(true);
mActivity = getActivity();
mLogoutBtn = (Button) mActivity.findViewById(R.id.logoutButton);
// how to inject engineMock to Activity under test?
ObjectGraph.create(new TestModule()).inject(this.mActivity);
assertTrue(mLogoutBtn.isEnabled() == true);
}
}
I use Mockito and Dagger for functional testing.
The key concept is that your test class inherits from ActivityUnitTestCase, instead of ActivityInstrumentationTestCase2; the latter super-class call onStart() life-cycle method of Activity blocking you for inject your test doubles dependencies, but with first super-class you can handle the life-cycle more fine-grained.
You can see my working examples using dagger-1.0.0 and mockito for test Activities and Fragments in:
https://github.com/IIIRepublica/android-civicrm-test
The project under test is in:
https://github.com/IIIRepublica/android-civicrm
Hope this helps you
I did some more experimenting and found out that Dagger is not able to create activity correctly when it is injected to test. In the new version of test, testDoSomethingCalledOnEngine passes but onCreate is not called on the MainActivity. The second test, testDoSomethingUI fails and there are actually two instances of MainActivity, onCreate gets called to the other instance (created by ActivityInstrumentationTestCase2 I quess) but not to the other. Maybe the developers at Square only thought about testing Activites with Robolectric instead of Android instrumentation test?
public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity> {
#Inject Engine engineMock;
#Inject MainActivity mActivity;
public MainActivityTest() {
super(MainActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
// Inject engineMock to test & Activity under test
ObjectGraph.create(new TestModule()).inject(this);
}
#Module(
includes = MainModule.class,
entryPoints = MainActivityTest.class,
overrides = true
)
static class TestModule {
#Provides
#Singleton
Engine provideEngine() {
return mock(Engine.class);
}
}
public void testDoSomethingCalledOnEngine() {
when(engineMock.isLoggedIn()).thenReturn(true);
mActivity.onSomethingHappened();
verify(engineMock).doSomething();
}
#UiThreadTest
public void testDoSomethingUI() {
when(engineMock.isLoggedIn()).thenReturn(true);
mActivity.onSomethingHappened();
Button btn = (Button) mActivity.findViewById(R.id.logoutButton);
String btnText = btn.getText().toString();
assertTrue(btnText.equals("Log out"));
}
}
I have put everything together and made demo app that shows how to test with dagger: https://github.com/vovkab/dagger-unit-test
Here is my pervious answer with more details:
https://stackoverflow.com/a/24393265/369348

Categories

Resources