Slide 1

Slide 1 text

on production Dagger 2

Slide 2

Slide 2 text

@froger_mcs Head of mobile @ Azimo

Slide 3

Slide 3 text

How to start with Dagger 2 • The Future of Dependency Injection with Dagger 2 (Jake Wharton)
 https://goo.gl/PPqT51 • DAGGER 2 - A New Type of dependency injection (Gregory Kick)
 https://goo.gl/UVBvaH • Bunch of posts about Dagger 2 (DI, basics, scopes, performance)
 http://frogermcs.github.io/

Slide 4

Slide 4 text

Dagger 2 basic usage

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

public class SplashActivity extends BaseActivity {
 
 private SplashActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder()
 .setClient(new OkClient(MyOkHttpClient.getInstance()))
 .setEndpoint(getString(R.string.endpoint));
 RestAdapter restAdapter = restAdapterBuilder.build();
 
 GithubApiService githubApiService = restAdapter.create(GithubApiService.class);
 
 UserManager userManager = new UserManager(githubApiService);
 
 presenter = new SplashActivityPresenter(this, Validator.getInstance(), userManager);
 }
 } without DI/Dagger 2

Slide 7

Slide 7 text

public class SplashActivity extends BaseActivity {
 
 @Inject
 SplashActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 getSplashActivityComponent().inject(this);
 }
 } with DI/Dagger 2

Slide 8

Slide 8 text

public class SplashActivity extends BaseActivity {
 
 @Inject
 SplashActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 getSplashActivityComponent().inject(this);
 }
 } with DI/Dagger 2

Slide 9

Slide 9 text

public class SplashActivity extends BaseActivity {
 
 @Inject
 SplashActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 getSplashActivityComponent().inject(this);
 }
 } No magic happens here Initialization takes place elsewhere with DI/Dagger 2

Slide 10

Slide 10 text

Where?

Slide 11

Slide 11 text

Initialization/usage separation SplashActivityModule SplashActivity SplashActivityPresenter AppModule Application Validator ApiModule OkhttpClient RestAdapter GithubApiService UserManager SplashActivtity SplashActivityPresenter @Inject SplashActivtityPresenter SplashActivity Validator UserManager @Inject Initialization Usage

Slide 12

Slide 12 text

@Module
 public class ApiModule {
 
 @Provides
 @Singleton
 OkHttpClient provideOkHttpClient() {
 OkHttpClient okHttpClient = new OkHttpClient();
 okHttpClient.setConnectTimeout(60 * 1000, TimeUnit.MILLISECONDS);
 okHttpClient.setReadTimeout(60 * 1000, TimeUnit.MILLISECONDS);
 return okHttpClient;
 }
 
 @Provides
 @Singleton
 RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
 RestAdapter.Builder builder = new RestAdapter.Builder();
 builder.setClient(new OkClient(okHttpClient))
 .setEndpoint(application.getString(R.string.endpoint));
 return builder.build();
 }
 
 @Provides
 @Singleton
 GithubApiService provideGithubApiService(RestAdapter restAdapter) {
 return restAdapter.create(GithubApiService.class);
 }
 } @Module Example

Slide 13

Slide 13 text

Injection process demystified

Slide 14

Slide 14 text

SplashActivityModule SplashActivity SplashActivityPresenter SplashActivtity SplashActivityPresenter @Inject ?

Slide 15

Slide 15 text

public class SplashActivity extends BaseActivity {
 
 @Inject
 SplashActivityPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 getSplashActivityComponent().inject(this);
 }
 } DI with Dagger 2 @Module
 public class SplashActivityModule {
 private SplashActivity splashActivity;
 
 public SplashActivityModule(SplashActivity splashActivity) {
 this.splashActivity = splashActivity;
 }
 
 @Provides
 @ActivityScope
 SplashActivity provideSplashActivity() {
 return splashActivity;
 }
 
 @Provides
 @ActivityScope
 SplashActivityPresenter
 provideSplashActivityPresenter(Validator validator, UserManager userManager) {
 return new SplashActivityPresenter(splashActivity, validator, userManager);
 }
 } ?

Slide 16

Slide 16 text

SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component SplashActivtity SplashActivityPresenter @Inject

Slide 17

Slide 17 text

SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component SplashActivtity SplashActivityPresenter @Inject @ActivityScope
 @Component(
 modules = SplashActivityModule.class,
 dependencies = AppComponent.class
 )
 public interface SplashActivityComponent {
 SplashActivity inject(SplashActivity splashActivity);
 }

Slide 18

Slide 18 text

SplashActivityComponent SplashActivtity SplashActivityPresenter @Inject @Generated("dagger.internal.codegen.ComponentProcessor")
 public final class DaggerSplashActivityComponent
 implements SplashActivityComponent {
 private Provider getValidatorProvider;
 private Provider getUserManagerProvider;
 private Provider provideSplashActivityPresenterProvider;
 private MembersInjector splashActivityMembersInjector;
 
 private void initialize(final Builder builder) {
 //...
 }
 
 @Override
 public SplashActivity inject(SplashActivity splashActivity) {
 splashActivityMembersInjector.injectMembers(splashActivity);
 return splashActivity;
 }
 
 //...
 }

Slide 19

Slide 19 text

MembersInjector SplashActivityComponent SplashActivtity SplashActivityPresenter @Inject @Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivity_MembersInjector
 implements MembersInjector {
 private final MembersInjector supertypeInjector;
 private final Provider presenterProvider;
 
 //...
 
 @Override
 public void injectMembers(SplashActivity instance) {
 if (instance == null) {
 throw new NullPointerException(/*...*/);
 }
 supertypeInjector.injectMembers(instance);
 instance.presenter = presenterProvider.get();
 }
 
 //...
 }

Slide 20

Slide 20 text

SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject ?

Slide 21

Slide 21 text

Factory SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject

Slide 22

Slide 22 text

Factory SplashActivity Component MembersInjector @Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivityModule_SplashActivityPresenterFactory
 implements Factory {
 private final SplashActivityModule module;
 private final Provider validatorProvider;
 private final Provider userManagerProvider;
 
 //...
 
 @Override
 public SplashActivityPresenter get() {
 SplashActivityPresenter provided = module.splashActivityPresenter(
 validatorProvider.get(), userManagerProvider.get()
 );
 if (provided == null) {
 throw new NullPointerException(/*...*/);
 }
 return provided;
 }
 
 //...
 }

Slide 23

Slide 23 text

Factory SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject Basic injection flow

Slide 24

Slide 24 text

Alternative for scope instances or singletons ScopedProvider Factory SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject ScopedProvider SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject SplashActivityPresenter single instance 1st inject 2nd inject

Slide 25

Slide 25 text

Injections interface

Slide 26

Slide 26 text

SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component SplashActivtity SplashActivityPresenter @Inject @ActivityScope
 @Component(
 modules = SplashActivityModule.class,
 dependencies = AppComponent.class
 )
 public interface SplashActivityComponent {
 SplashActivity inject(SplashActivity splashActivity);
 }

Slide 27

Slide 27 text

@Singleton
 @Component(modules = {
 AzimoAppModule.class,
 //...
 })
 public interface AzimoAppComponent {
 void inject(AzimoApplication app);
 
 void inject(IntentUriHandlerActivity intentUriHandlerActivity);
 void inject(MyContactsActivity myContactsActivity);
 void inject(SelectItemActivity selectItemActivity);
 
 void inject(ProgressFragment progressFragment);
 
 void inject(GcmRegistrationService gcmRegistrationService);
 void inject(SyncConfigService syncConfigService);
 void inject(GcmMessagingService gcmMessagingService);
 
 void inject(LocaleChangeReceiver localeChangeReceiver);
 void inject(ReferrerReceiver referrerReceiver);
 void inject(ReminderReceiver reminderReceiver);
 
 void inject(RateSessionExpiredDialog rateSessionExpiredDialog);
 void inject(LeaveReviewDialog leaveReviewDialog);
 void inject(EnjoyingAppDialog enjoyingAppDialog);
 void inject(MakeSuggestionsDialog makeSuggestionsDialog);
 
 //… 
 } Production app example

Slide 28

Slide 28 text

What about base class injections? @Singleton
 @Component(modules = {
 AzimoAppModule.class,
 //...
 })
 public interface AzimoAppComponent {
 void inject(Application app);
 
 void inject(BaseActivity baseActivity);
 
 void inject(BaseFragment baseFragment);
 
 void inject(Service service);
 
 void inject(BroadcastReceiver broadcastReceiver);
 
 void inject(BaseDialogFragment baseDialogFragment);
 
 }

Slide 29

Slide 29 text

public class BaseActivity extends AppCompatActivity {
 
 @Inject
 AnalyticsTracker analyticsTracker;
 
 }
 
 public class SplashActivity extends BaseActivity {
 
 @Inject
 Validator validator;
 
 } SplashActivtity SplashActivityPresenter @Inject BaseActivtity AnalyticsTracker @Inject

Slide 30

Slide 30 text

public class BaseActivity extends AppCompatActivity {
 
 @Inject
 AnalyticsTracker analyticsTracker;
 
 }
 
 public class SplashActivity extends BaseActivity {
 
 @Inject
 Validator validator;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 getComponent().inject(this);
 }
 } Subclass injection

Slide 31

Slide 31 text

@Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivity_MembersInjector
 implements MembersInjector {
 private final MembersInjector supertypeInjector;
 private final Provider presenterProvider;
 
 //...
 
 @Override
 public void injectMembers(SplashActivity instance) {
 if (instance == null) {
 throw new NullPointerException(/*...*/);
 }
 supertypeInjector.injectMembers(instance);
 instance.presenter = presenterProvider.get();
 }
 
 //...
 }

Slide 32

Slide 32 text

public class BaseActivity extends AppCompatActivity {
 
 @Inject
 AnalyticsTracker analyticsTracker;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 getComponent().inject(this);
 }
 }
 
 public class SplashActivity extends BaseActivity {
 
 @Inject
 Validator validator;
 
 } Super class injection

Slide 33

Slide 33 text

Super class injection • ComponentReflectionInjector 
 https://gist.github.com/konmik/ 6ac725fa7134402539c4 • Uses Reflection • Performance drawback is about 0.013 ms per injection on slow device

Slide 34

Slide 34 text

• 0.013 ms per injection (50-methods component, tested on Samsung Galaxy S) • 60 FPS = 16ms per frame • => ~100 reflected injections per frame • (1000 direct injections per frame) ComponentReflectionInjector

Slide 35

Slide 35 text

ComponentReflectionInjector public class BaseActivity extends AppCompatActivity {
 
 @Inject
 AnalyticsTracker analyticsTracker;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 componentReflectionInjector.inject(this);
 }
 }
 
 public class SplashActivity extends BaseActivity {
 
 @Inject
 Validator validator;
 
 } Super class injection

Slide 36

Slide 36 text

But still no… @Singleton
 @Component(modules = {
 AzimoAppModule.class,
 //...
 })
 public interface AzimoAppComponent {
 void inject(Application app);
 
 void inject(BaseActivity baseActivity);
 
 void inject(BaseFragment baseFragment);
 
 void inject(Service service);
 
 void inject(BroadcastReceiver broadcastReceiver);
 
 void inject(BaseDialogFragment baseDialogFragment);
 
 }

Slide 37

Slide 37 text

Injection types

Slide 38

Slide 38 text

Direct injection public class SplashActivity {
 
 @Inject
 Validator validator;
 
 //...
 } SplashActivtity Validator

Slide 39

Slide 39 text

Lazy injection public class SplashActivity {
 
 @Inject
 Lazy lazyValidator;
 
 //...
 } SplashActivtity Validator Lazy 1st get() call 2nd get() call get() SplashActivtity Dependencies Graph Validator Lazy

Slide 40

Slide 40 text

Lazy injection public class SplashActivity {
 
 @Inject
 Lazy lazyValidator;
 
 //...
 } SplashActivtity Validator Lazy 1st get() call nth get() call get() SplashActivtity Dependencies Graph Validator Lazy Same instance like in 1st call …

Slide 41

Slide 41 text

Launch time without lazy loading: 650ms SplashActivity Crashlytics 200ms Mixpanel Google Analytics 100ms Rest Client 150ms 200ms SplashActivity Crashlytics 200ms Mixpanel Google Analytics 100ms Rest Client 150ms 200ms Launch time with lazy loading: 100ms Those will be Lazy-loaded Lazy injection
 app launch time optimizations More optimization hints -> http://frogermcs.github.io/dagger-graph-creation-performance/

Slide 42

Slide 42 text

Provider injection public class SplashActivity {
 
 @Inject
 Provider validatorProvider;
 
 //...
 } 1st call 2nd call get() (instance 1) SplashActivtity Dependencies Graph Validator Provider Validator Validator Provider get() (instance 2) SplashActivtity Dependencies Graph Validator

Slide 43

Slide 43 text

Provider injection 1st get() call nth get() call … get() (instance 1) SplashActivtity Dependencies Graph Validator Provider Validator Validator Provider get() (instance n) SplashActivtity Dependencies Graph Validator New instance every time public class SplashActivity {
 
 @Inject
 Provider validatorProvider;
 
 //...
 }

Slide 44

Slide 44 text

What about „singletons"?

Slide 45

Slide 45 text

ScopedProvider Factory SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject

Slide 46

Slide 46 text

@Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivityModule_SplashActivityPresenterFactory
 implements Factory {
 private final SplashActivityModule module;
 private final Provider validatorProvider;
 private final Provider userManagerProvider;
 
 //...
 
 private void initialize(final Builder builder) { //… this.validatorProvider = Validator_Factory.create(/*...*/) this.userManagerProvider = ScopedProvider.create( UserManager_Factory.create(/*...*/) ); //... } 
 //...
 }

Slide 47

Slide 47 text

@Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivityModule_SplashActivityPresenterFactory
 implements Factory {
 private final SplashActivityModule module;
 private final Provider validatorProvider;
 private final Provider userManagerProvider;
 
 //...
 
 private void initialize(final Builder builder) { //… this.validatorProvider = Validator_Factory.create(/*...*/) this.userManagerProvider = ScopedProvider.create( UserManager_Factory.create(/*...*/) ); //... } 
 //...
 }

Slide 48

Slide 48 text

ScopedProvider public final class ScopedProvider implements Provider {
 private static final Object UNINITIALIZED = new Object(); 
 private final Factory factory;
 private volatile Object instance = UNINITIALIZED;
 
 @Override
 public T get() {
 // double-check idiom from EJ2: Item 71
 Object result = instance;
 if (result == UNINITIALIZED) {
 synchronized (this) {
 result = instance;
 if (result == UNINITIALIZED) {
 instance = result = factory.get();
 }
 }
 }
 return (T) result;
 } //…
 }

Slide 49

Slide 49 text

ScopedProvider public final class ScopedProvider implements Provider {
 private static final Object UNINITIALIZED = new Object(); 
 private final Factory factory;
 private volatile Object instance = UNINITIALIZED;
 
 @Override
 public T get() {
 // double-check idiom from EJ2: Item 71
 Object result = instance;
 if (result == UNINITIALIZED) {
 synchronized (this) {
 result = instance;
 if (result == UNINITIALIZED) {
 instance = result = factory.get();
 }
 }
 }
 return (T) result;
 } //…
 }

Slide 50

Slide 50 text

@Singleton =/= singleton

Slide 51

Slide 51 text

@Singleton == single instance 
 per component

Slide 52

Slide 52 text

Scopes

Slide 53

Slide 53 text

@ApplicationScope @UserScope @ActivityScope Scope types

Slide 54

Slide 54 text

@ApplicationScope @UserScope @ActivityScope @Lorem @Ipsum @Dolor = It’s just a name. Not a difference at all.

Slide 55

Slide 55 text

@Scope
 public @interface UserScope { 
 } Scope definition

Slide 56

Slide 56 text

@UserScope
 @Subcomponent(modules = UserModule.class)
 public interface UserComponent {
 
 } Scope usage @Module
 public class UserModule {
 
 @Provides
 @UserScope
 RepositoriesManager provideRepositoriesManager(User user, GithubApiService apiService) {
 return new RepositoriesManager(user, apiService);
 } 
 }

Slide 57

Slide 57 text

Scopes example

Slide 58

Slide 58 text

Creating scopes • Subcomponents • Components dependencies

Slide 59

Slide 59 text

@Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )
 public interface AppComponent {
 
 } Singleton (Application scope) AppComponent - AppModule - GithubApiModule Creating scopes with subcomponents

Slide 60

Slide 60 text

@Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )
 public interface AppComponent {
 
 UserComponent plus(UserModule userModule);
 
 } UserScope UserComponent - UserModule Singleton (Application scope) AppComponent - AppModule - GithubApiModule @UserScope
 @Subcomponent(
 modules = {
 UserModule.class
 }
 )
 public interface UserComponent {
 
 }

Slide 61

Slide 61 text

@UserScope
 @Subcomponent(
 modules = {
 UserModule.class
 }
 )
 public interface UserComponent {
 SplashActivityComponent plus(
 SplashActivityModule module
 );
 } ActivityScope ActivityComponent - ...ActivityModules UserScope UserComponent - UserModule Singleton (Application scope) AppComponent - AppModule - GithubApiModule … @ActivityScope
 @Subcomponent(
 modules = SplashActivityModule.class
 )
 public interface SplashActivityComponent {
 
 }

Slide 62

Slide 62 text

Creating scopes with components dependencies UserScope UserComponent - UserModule Singleton (Application scope) AppComponent - AppModule - GithubApiModule

Slide 63

Slide 63 text

Creating scopes with components dependencies @Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )
 public interface AppComponent {
 Validator Validator();
 UserManager getUserManager();
 AnalyticsManager getAnalyticsManager();
 GithubApiService getGithubApiService();
 } @UserScope
 @Component(
 modules = {
 UserModule.class
 },
 dependencies = AppComponent.class
 )
 public interface UserComponent {
 }

Slide 64

Slide 64 text

Creating scopes with components dependencies @Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )
 public interface AppComponent {
 Validator Validator();
 UserManager getUserManager();
 AnalyticsManager getAnalyticsManager();
 GithubApiService getGithubApiService();
 } @UserScope
 @Component(
 modules = {
 UserModule.class
 },
 dependencies = AppComponent.class
 )
 public interface UserComponent {
 } All dependencies used in dependent components have to be exposed

Slide 65

Slide 65 text

Testing

Slide 66

Slide 66 text

Unit tests • Dagger 2 doesn't make unit testing easier • …but DI itself makes project ready for testing • (Still) no official way from Dagger 2 team • Dagger 2 + Robolectric 3

Slide 67

Slide 67 text

Swapping modules public class TestGithubClientApplication
 extends GithubClientApplication {
 
 AppModule appModule;
 
 @Override
 public AppModule getAppModule() {
 if (appModule == null) {
 return super.getAppModule();
 }
 return appModule;
 }
 
 public void setAppModule(AppModule appModule) {
 this.appModule = appModule;
 initAppComponent();
 }
 } Dagger 2 + Robolectric 3 public class GithubClientApplication extends Application {
 
 private AppComponent appComponent;
 
 @Override
 public void onCreate() {
 super.onCreate();
 initAppComponent();
 }
 
 protected void initAppComponent() {
 appComponent = DaggerAppComponent.builder()
 .appModule(getAppModule())
 .build();
 } 
 @VisibleForTesting
 public AppModule getAppModule() {
 return new AppModule(this);
 }
 } App Tests

Slide 68

Slide 68 text

Swapping modules @Module
 public class AppModule {
 private Application application;
 
 public AppModule(Application application) {
 this.application = application;
 }
 
 @Provides
 @Singleton
 public Application provideApplication() {
 return application;
 }
 
 @Provides
 @Singleton
 AnalyticsManager provideAnalyticsManager() {
 return new AnalyticsManager(application);
 }
 } Dagger 2 + Robolectric 3 public class MockAppModule extends AppModule {
 @Mock
 AnalyticsManager analyticsManagerMock;
 
 public MockAppModule(Application application) {
 super(application);
 MockitoAnnotations.initMocks(this);
 }
 
 @Override
 AnalyticsManager provideAnalyticsManager() {
 return analyticsManagerMock;
 }
 } App Tests

Slide 69

Slide 69 text

Swapping modules @RunWith(RobolectricGradleTestRunner.class)
 @Config(
 sdk = 18,
 constants = BuildConfig.class,
 application = TestGithubClientApplication.class
 )
 public class SplashActivityTests {
 
 @Before
 public void setup() {
 TestGithubClientApplication app = (TestGithubClientApplication) RuntimeEnvironment.application;
 MockAppModule mockAppModule = new MockAppModule(app);
 app.setAppModule(mockAppModule);
 }
 
 @Test
 public void testName() throws Exception {
 SplashActivity activity = Robolectric.setupActivity(SplashActivity.class);
 verify(activity.analyticsManager).logScreenView(anyString());
 }
 } Dagger 2 + Robolectric 3

Slide 70

Slide 70 text

Injecting without Dagger public class TestGithubClientApplication
 extends GithubClientApplication {
 
 private AppComponent appComponent;
 private SplashActivityComponent splashActivityComponent;
 
 @Override
 public AppComponent getAppComponent() {
 if (appComponent == null) {
 appComponent = mock(AppComponent.class);
 when(appComponent.plus(any(SplashActivityModule.class)))
 .thenReturn(splashActivityComponent);
 }
 
 return appComponent;
 }
 
 public void setSplashActivityComponent(SplashActivityComponent component) {
 this.splashActivityComponent = component;
 }
 }

Slide 71

Slide 71 text

@RunWith(RobolectricGradleTestRunner.class)
 @Config(
 sdk = 18,
 constants = BuildConfig.class,
 application = TestGithubClientApplication.class
 )
 public class SplashActivityTests {
 
 @Mock
 SplashActivityComponent splashActivityComponentMock;
 @Mock
 AnalyticsManager analyticsManagerMock;
 
 @Before
 public void setup() {
 MockitoAnnotations.initMocks(this);
 
 doAnswer(new Answer() {
 @Override
 public Object answer(InvocationOnMock invocation) {
 SplashActivity activity = (SplashActivity) invocation.getArguments()[0];
 activity.analyticsManager = analyticsManagerMock;
 return null;
 }
 }).when(splashActivityComponentMock).inject(any(SplashActivity.class));
 
 TestGithubClientApplication app = (TestGithubClientApplication) RuntimeEnvironment.application;
 app.setSplashActivityComponent(splashActivityComponentMock);
 }
 
 @Test
 public void testName() throws Exception {
 SplashActivity activity = Robolectric.setupActivity(SplashActivity.class);
 verify(activity.analyticsManager).logScreenView(anyString());
 }
 }

Slide 72

Slide 72 text

@RunWith(RobolectricGradleTestRunner.class)
 @Config(
 sdk = 18,
 constants = BuildConfig.class,
 application = TestGithubClientApplication.class
 )
 public class SplashActivityTests {
 
 @Mock
 SplashActivityComponent splashActivityComponentMock;
 @Mock
 AnalyticsManager analyticsManagerMock;
 
 @Before
 public void setup() {
 MockitoAnnotations.initMocks(this);
 
 doAnswer(new Answer() {
 @Override
 public Object answer(InvocationOnMock invocation) {
 SplashActivity activity = (SplashActivity) invocation.getArguments()[0];
 activity.analyticsManager = analyticsManagerMock;
 return null;
 }
 }).when(splashActivityComponentMock).inject(any(SplashActivity.class));
 
 TestGithubClientApplication app = (TestGithubClientApplication) RuntimeEnvironment.application;
 app.setSplashActivityComponent(splashActivityComponentMock);
 }
 
 @Test
 public void testName() throws Exception {
 SplashActivity activity = Robolectric.setupActivity(SplashActivity.class);
 verify(activity.analyticsManager).logScreenView(anyString());
 }
 }

Slide 73

Slide 73 text

Integration tests Espresso + Mockito + Dagger 2

Slide 74

Slide 74 text

Do we really need dagger?

Slide 75

Slide 75 text

Case #1 (response 200)

Slide 76

Slide 76 text

Testable Case #1 (response 200)

Slide 77

Slide 77 text

Case #2 (response 400)

Slide 78

Slide 78 text

Testable Case #2 (response 400)

Slide 79

Slide 79 text

Case #3 (response 500)

Slide 80

Slide 80 text

Not testable Case #3 (response 500)

Slide 81

Slide 81 text

Case #4 (no internet connection)

Slide 82

Slide 82 text

Not testable Case #4 (no internet connection)

Slide 83

Slide 83 text

Solution?

Slide 84

Slide 84 text

Mock API client

Slide 85

Slide 85 text

Instrumentation tests mocking public class ApplicationMock extends GithubClientApplication {
 
 private AppComponent appComponent;
 private GithubApiModule githubApiModuleMock;
 
 public void setGithubApiModuleMock(GithubApiModule githubApiModuleMock) {
 this.githubApiModuleMock = githubApiModuleMock;
 setupMockAppComponent();
 }
 
 public void setupMockAppComponent() {
 appComponent = DaggerAppComponent.builder()
 .appModule(new AppModule(this))
 .githubApiModule(githubApiModuleMock)
 .build();
 }
 
 @Override
 public AppComponent getAppComponent() {
 return appComponent == null ? super.getAppComponent() : appComponent;
 }
 }

Slide 86

Slide 86 text

Instrumentation tests mocking @RunWith(AndroidJUnit4.class)
 public class SplashActivityUITests {
 
 @Mock UserManager userManagerMock;
 
 @Rule public ActivityTestRule activityRule = new ActivityTestRule<>(
 SplashActivity.class, true, false
 );
 
 @Before
 public void setUp() {
 MockitoAnnotations.initMocks(this);
 GithubApiModuleMock githubApiModuleMock = new GithubApiModuleMock(userManagerMock);
 ApplicationMock app = (ApplicationMock) InstrumentationRegistry.getTargetContext().getApplicationContext();
 app.setGithubApiModuleMock(githubApiModuleMock);
 activityRule.launchActivity(new Intent());
 }
 
 @Test
 public void checkLoadingError() {
 Mockito.when(userManagerMock.getUser(anyString()))
 .thenReturn(Observable.error(new RuntimeException("test")));
 
 onView(withId(R.id.etUsername)).perform(typeText("frogermcs"));
 onView(withId(R.id.btnShowRepositories)).perform(click());
 
 onView(withId(R.id.etUsername)).check(matches(hasErrorText("Validation error")));
 }
 }

Slide 87

Slide 87 text

Instrumentation tests mocking @RunWith(AndroidJUnit4.class)
 public class SplashActivityUITests {
 
 @Mock UserManager userManagerMock;
 
 @Rule public ActivityTestRule activityRule = new ActivityTestRule<>(
 SplashActivity.class, true, false
 );
 
 @Before
 public void setUp() {
 MockitoAnnotations.initMocks(this);
 GithubApiModuleMock githubApiModuleMock = new GithubApiModuleMock(userManagerMock);
 ApplicationMock app = (ApplicationMock) InstrumentationRegistry.getTargetContext().getApplicationContext();
 app.setGithubApiModuleMock(githubApiModuleMock);
 activityRule.launchActivity(new Intent());
 }
 
 @Test
 public void checkLoadingError() {
 Mockito.when(userManagerMock.getUser(anyString()))
 .thenReturn(Observable.error(new RuntimeException("test")));
 
 onView(withId(R.id.etUsername)).perform(typeText("frogermcs"));
 onView(withId(R.id.btnShowRepositories)).perform(click());
 
 onView(withId(R.id.etUsername)).check(matches(hasErrorText("Validation error")));
 }
 }

Slide 88

Slide 88 text

Example https://github.com/frogermcs/GithubClient Dagger 2, scopes, unit and instrumentation tests

Slide 89

Slide 89 text

http://blog.codinghorror.com/learn-to-read-the-source-luke/ „If you can't understand the platform below you, how can you understand your own software?”

Slide 90

Slide 90 text

Thanks! @froger_mcs