Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dagger 2 on production

Dagger 2 on production

Bce40bbee247856d407f1d732fda01c0?s=128

Mirosław Stanek

October 17, 2015
Tweet

More Decks by Mirosław Stanek

Other Decks in Programming

Transcript

  1. on production Dagger 2

  2. @froger_mcs Head of mobile @ Azimo

  3. 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/
  4. Dagger 2 basic usage

  5. None
  6. 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
  7. public class SplashActivity extends BaseActivity {
 
 @Inject
 SplashActivityPresenter presenter;


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


    
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 getSplashActivityComponent().inject(this);
 }
 } with DI/Dagger 2
  9. 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
  10. Where?

  11. Initialization/usage separation SplashActivityModule SplashActivity SplashActivityPresenter AppModule Application Validator ApiModule OkhttpClient

    RestAdapter GithubApiService UserManager SplashActivtity SplashActivityPresenter @Inject SplashActivtityPresenter SplashActivity Validator UserManager @Inject Initialization Usage
  12. @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
  13. Injection process demystified

  14. SplashActivityModule SplashActivity SplashActivityPresenter SplashActivtity SplashActivityPresenter @Inject ?

  15. 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);
 }
 } ?
  16. SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component SplashActivtity SplashActivityPresenter @Inject

  17. SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component SplashActivtity SplashActivityPresenter @Inject @ActivityScope
 @Component(


    modules = SplashActivityModule.class,
 dependencies = AppComponent.class
 )
 public interface SplashActivityComponent {
 SplashActivity inject(SplashActivity splashActivity);
 }
  18. SplashActivityComponent SplashActivtity SplashActivityPresenter @Inject @Generated("dagger.internal.codegen.ComponentProcessor")
 public final class DaggerSplashActivityComponent
 implements

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


    implements MembersInjector<SplashActivity> {
 private final MembersInjector<AppCompatActivity> supertypeInjector;
 private final Provider<SplashActivityPresenter> presenterProvider;
 
 //...
 
 @Override
 public void injectMembers(SplashActivity instance) {
 if (instance == null) {
 throw new NullPointerException(/*...*/);
 }
 supertypeInjector.injectMembers(instance);
 instance.presenter = presenterProvider.get();
 }
 
 //...
 }
  20. SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject ?

  21. Factory<SplashActivityPresenter> SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject

  22. Factory<SplashActivityPresenter> SplashActivity Component MembersInjector @Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivityModule_SplashActivityPresenterFactory
 implements

    Factory<SplashActivityPresenter> {
 private final SplashActivityModule module;
 private final Provider<Validator> validatorProvider;
 private final Provider<UserManager> userManagerProvider;
 
 //...
 
 @Override
 public SplashActivityPresenter get() {
 SplashActivityPresenter provided = module.splashActivityPresenter(
 validatorProvider.get(), userManagerProvider.get()
 );
 if (provided == null) {
 throw new NullPointerException(/*...*/);
 }
 return provided;
 }
 
 //...
 }
  23. Factory<SplashActivityPresenter> SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject

    Basic injection flow
  24. Alternative for scope instances or singletons ScopedProvider <SplashActivityPresenter> Factory<SplashActivityPresenter> SplashActivityModule

    SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject ScopedProvider <SplashActivityPresenter> SplashActivity Component MembersInjector SplashActivtity SplashActivityPresenter @Inject SplashActivityPresenter single instance 1st inject 2nd inject
  25. Injections interface

  26. SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component SplashActivtity SplashActivityPresenter @Inject @ActivityScope
 @Component(


    modules = SplashActivityModule.class,
 dependencies = AppComponent.class
 )
 public interface SplashActivityComponent {
 SplashActivity inject(SplashActivity splashActivity);
 }
  27. @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
  28. 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);
 
 }
  29. public class BaseActivity extends AppCompatActivity {
 
 @Inject
 AnalyticsTracker analyticsTracker;


    
 }
 
 public class SplashActivity extends BaseActivity {
 
 @Inject
 Validator validator;
 
 } SplashActivtity SplashActivityPresenter @Inject BaseActivtity AnalyticsTracker @Inject
  30. 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
  31. @Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivity_MembersInjector
 implements MembersInjector<SplashActivity> {
 private final

    MembersInjector<AppCompatActivity> supertypeInjector;
 private final Provider<SplashActivityPresenter> presenterProvider;
 
 //...
 
 @Override
 public void injectMembers(SplashActivity instance) {
 if (instance == null) {
 throw new NullPointerException(/*...*/);
 }
 supertypeInjector.injectMembers(instance);
 instance.presenter = presenterProvider.get();
 }
 
 //...
 }
  32. 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
  33. Super class injection • ComponentReflectionInjector<T> 
 https://gist.github.com/konmik/ 6ac725fa7134402539c4 • Uses

    Reflection • Performance drawback is about 0.013 ms per injection on slow device
  34. • 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
  35. 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
  36. 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);
 
 }
  37. Injection types

  38. Direct injection public class SplashActivity {
 
 @Inject
 Validator validator;


    
 //...
 } SplashActivtity Validator
  39. Lazy injection public class SplashActivity {
 
 @Inject
 Lazy<Validator> lazyValidator;


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


    
 //...
 } SplashActivtity Validator Lazy<Validator> 1st get() call nth get() call get() SplashActivtity Dependencies Graph Validator Lazy<Validator> Same instance like in 1st call …
  41. 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/
  42. Provider injection public class SplashActivity {
 
 @Inject
 Provider<Validator> validatorProvider;


    
 //...
 } 1st call 2nd call get() (instance 1) SplashActivtity Dependencies Graph Validator Provider<Validator> Validator Validator Provider<Validator> get() (instance 2) SplashActivtity Dependencies Graph Validator
  43. Provider injection 1st get() call nth get() call … get()

    (instance 1) SplashActivtity Dependencies Graph Validator Provider<Validator> Validator Validator Provider<Validator> get() (instance n) SplashActivtity Dependencies Graph Validator New instance every time public class SplashActivity {
 
 @Inject
 Provider<Validator> validatorProvider;
 
 //...
 }
  44. What about „singletons"?

  45. ScopedProvider <SplashActivityPresenter> Factory<SplashActivityPresenter> SplashActivityModule SplashActivity SplashActivityPresenter SplashActivity Component MembersInjector SplashActivtity

    SplashActivityPresenter @Inject
  46. @Generated("dagger.internal.codegen.ComponentProcessor")
 public final class SplashActivityModule_SplashActivityPresenterFactory
 implements Factory<SplashActivityPresenter> {
 private final

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

    SplashActivityModule module;
 private final Provider<Validator> validatorProvider;
 private final Provider<UserManager> userManagerProvider;
 
 //...
 
 private void initialize(final Builder builder) { //… this.validatorProvider = Validator_Factory.create(/*...*/) this.userManagerProvider = ScopedProvider.create( UserManager_Factory.create(/*...*/) ); //... } 
 //...
 }
  48. ScopedProvider public final class ScopedProvider<T> implements Provider<T> {
 private static

    final Object UNINITIALIZED = new Object(); 
 private final Factory<T> 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;
 } //…
 }
  49. ScopedProvider public final class ScopedProvider<T> implements Provider<T> {
 private static

    final Object UNINITIALIZED = new Object(); 
 private final Factory<T> 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;
 } //…
 }
  50. @Singleton =/= singleton

  51. @Singleton == single instance 
 per component

  52. Scopes

  53. @ApplicationScope @UserScope @ActivityScope Scope types

  54. @ApplicationScope @UserScope @ActivityScope @Lorem @Ipsum @Dolor = It’s just a

    name. Not a difference at all.
  55. @Scope
 public @interface UserScope { 
 } Scope definition

  56. @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);
 } 
 }
  57. Scopes example

  58. Creating scopes • Subcomponents • Components dependencies

  59. @Singleton
 @Component(
 modules = {
 AppModule.class,
 GithubApiModule.class
 }
 )
 public

    interface AppComponent {
 
 } Singleton (Application scope) AppComponent - AppModule - GithubApiModule Creating scopes with subcomponents
  60. @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 {
 
 }
  61. @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 {
 
 }
  62. Creating scopes with components dependencies UserScope UserComponent - UserModule Singleton

    (Application scope) AppComponent - AppModule - GithubApiModule
  63. 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 {
 }
  64. 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
  65. Testing

  66. 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
  67. 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
  68. 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
  69. 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
  70. 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;
 }
 }
  71. @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());
 }
 }
  72. @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());
 }
 }
  73. Integration tests Espresso + Mockito + Dagger 2

  74. Do we really need dagger?

  75. Case #1 (response 200)

  76. Testable Case #1 (response 200)

  77. Case #2 (response 400)

  78. Testable Case #2 (response 400)

  79. Case #3 (response 500)

  80. Not testable Case #3 (response 500)

  81. Case #4 (no internet connection)

  82. Not testable Case #4 (no internet connection)

  83. Solution?

  84. Mock API client

  85. 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;
 }
 }
  86. Instrumentation tests mocking @RunWith(AndroidJUnit4.class)
 public class SplashActivityUITests {
 
 @Mock

    UserManager userManagerMock;
 
 @Rule public ActivityTestRule<SplashActivity> 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.<User>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")));
 }
 }
  87. Instrumentation tests mocking @RunWith(AndroidJUnit4.class)
 public class SplashActivityUITests {
 
 @Mock

    UserManager userManagerMock;
 
 @Rule public ActivityTestRule<SplashActivity> 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.<User>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")));
 }
 }
  88. Example https://github.com/frogermcs/GithubClient Dagger 2, scopes, unit and instrumentation tests

  89. 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?”
  90. Thanks! @froger_mcs