SlideShare a Scribd company logo
1 of 81
Download to read offline
@NYTDevs | developers.nytimes.com
Mike Nakhimovich @FriendlyMikhail
Android Framework Team
Swordfighting with Dagger
Dependecy Injection Made Less Simple
What is Dagger?
Alternative way to instantiate and manage your
objects
● Guice - Google (Dagger v.0)
● Dagger 1 - Square
● Dagger 2 - Back to Google :-)
Why Do We Need It?
Good Code = Testable Code
Why Do We Need It?
More Tests = Less Anxiety
Why Do We Need It?
Proper Code Organization is
a requirement for testing
Untestable Code (Me in the Beginning)
public class MyClass {
private Model model;
public MyClass() {this.model = new Model();}
public String getName() {return model.getName();}
}
How can we test if model.getName() was called?
Internet Told Me to Externalize My Dependencies
public class MyClass {
...
public MyClass(Model model) {this.model = model;}
public String getName() {return model.getName();}
}...
public void testGetData(){
Model model = mock(Model.class);
when(model.getName()).thenReturn("Mike");
MyClass myClass = new MyClass(model).getName();
verify(myClass.getName()).isEq("Mike");
}
Where Does Model Come From?
Dependency Injection
to the rescue!
Dagger Helps You Externalize Object Creation
@Provides
Model provideModel(){
return new Model();
}
Provide from Modules
@Module
public class AppModule{
}
A module is a part of your application that provides some
functionality.
Provide from Modules
@Module
public class AppModule{
@Provides
Model provideModel(){
return new Model();
}
...
A module is a part of your application that provides some
functionality.
Components are Composed of Modules
@Singleton
@Component(modules = {MyModule.class})
public interface AppComponent {
void inject(MyActivity activity);
}
A Component is the manager of all your module providers
Next, Create a Component Instance
component = DaggerAppComponent.builder()
.myModule(new MyModule(this))
.build();
Register with Component
protected void onCreate(Bundle savedInstanceState) {
getApplicationComponent().inject(this);
Injection Fun
Now you can inject dependencies as fields or
constructor arguments
@Inject
Model model;
@Inject
public Presenter(Model model)
Dagger @ NY Times
Now for the
fun stuff!
50% Recipes 50% Ramblings
Dagger @ NY Times
● Module/Component Architecture
○ Working with libraries
○ Build Types & Flavors
● Scopes
○ Application
○ Activity (Now with Singletons!)
● Testing
○ Espresso
○ Unit Testing
Code Organization
How Dagger manages 6 build variants & 6+ libraries
GoogleDebug
AmazonDebug
GoogleBetaAmazonBeta
GoogleRelease
AmazonRelease
Application Scoped Modules
● App Module
● Build Type Module
● Library Module
● Flavor Module
App Module Singletons
○ Parser (configured GSON)
App Module Singletons
○ Parser (configured GSON)
○ IO Managers
App Module Singletons
○ Parser (configured GSON)
○ IO Managers
○ Configs (Analytics,AB, E-Commerce)
Example Library Module: E-Commerce
@Module
public class ECommModule {
@Provides
@Singleton
public ECommBuilder provideECommBuilder( )
E-Comm using App Module’s Dep
@Module
public class ECommModule {
@Provides
@Singleton
public ECommBuilder provideECommBuilder(ECommConfig config){
return new ECommManagerBuilder().setConfig(config);
}
Amazon & Google Flavors
● Amazon Variants needs Amazon E-Commerce
● Google Variants needs to contain Google E-Commerce
How can Dagger help?
E-Comm Qualified Provider
@Module public class ECommModule {
@Provides @Singleton
public ECommBuilder provideECommBuilder(ECommConfig config){
return new ECommManagerBuilder().setConfig(config);
}
@Provides @Singleton @Google
public ECommManager providesGoogleEComm (ECommBuilder builder,
GooglePayments googlePayments)
E-Comm Qualified Provider
@Module public class ECommModule {
@Provides @Singleton
public ECommBuilder provideECommBuilder(ECommConfig config){
return new ECommManagerBuilder().setConfig(config);
}
...
@Provides @Singleton @Amazon
public ECommManager providesAmazonEComm (ECommBuilder builder,
AmazonPayments amazonPayments)
Flavor Module: src/google & src/Amazon
@Module public class FlavorModule {
}
Flavor Module Provides Non-Qualified E-Comm
@Module public class FlavorModule {
@Singleton
@Provides
ECommManager provideECommManager(@Google ECommManager ecomm)
}
Note: Proguard strips out the other impl from Jar :-)
Type Module
Brings build specific dependencies/providers in Type Module
Type Module
Brings build specific dependencies/providers in Type Module
○ Logging
■ Most logging for Beta Build
■ No-Op Release
Type Module
Brings build specific dependencies/providers in Type Module
○ Logging
■ Most logging for Beta Build
■ No-Op Release
○ Payments
■ No-Op for debug
Type Module
● Brings build specific dependencies/providers in Type Module
○ Logging
■ Most logging for Beta Build
■ No-Op Release
○ Payments
■ No-Op for debug
○ Device ID
■ Static for Debug
Component Composition
How we combine our modules
Start with Base Component
● Base Component lives in src/main
● Contains inject(T t) for classes & Services that register
with Dagger (non flavor/build specific)
interface BaseComponent {
void inject(NYTApplication target);
}
Src/Google & Src/Amazon Contain a FlavorComponent
● Create FlavorComponent that inherits from BaseComponent
● Register inject for flavor specific classes
● Anything not in src/flavor that needs component registers here ie:
○ Messaging Service
○ Payment Activity
public interface FlavorComponent extends BaseComponent {
void inject(ADMessaging target);
}
App Component debug, beta, release
One for src/debug src/beta src/release
public interface ApplicationComponent {
}
App Component
Inherits from Flavor Component
public interface ApplicationComponent extends FlavorComponent {
}
App Component
● Adds @Component @Singleton annotations
@Singleton @Component
public interface ApplicationComponent extends FlavorComponent {
}
App Component
● Adds modules
@Singleton @Component(modules =
{ApplicationModule.class, FlavorModule.class, TypeModule.class,
AnalyticsModule.class, ECommModule.class, PushClientModule.class })
public interface ApplicationComponent extends FlavorComponent {
}
Anything registering with App Component
gains access to all providers for the Flavor/Type
Usage of Generated App
Component
App Component Factory
public class ComponentFactory {
public AppComponent buildComponent(Application context) {
return componentBuilder(context).build();
}
// We override it for functional tests.
DaggerApplicationComponent.Builder componentBuilder(Application context) {
return DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(context)}
}
Component Instance
● NYT Application retains component
private void buildComponentAndInject() {
appComponent = componentFactory().buildComponent(this);
appComponent.inject(this);
}
public ComponentFactory componentFactory() {
return new ComponentFactory();
}
Introducing Activity Scope
Activity Component
● Inherits all “provides” from App Component
● Allows you to add “Activity Singletons”
○ 1 Per Activity
○ Many views/fragments within activity can inject same
instance
ActivityComponent
@Subcomponent(modules = {ActivityModule.class, BundleModule.class})
@ScopeActivity
public interface ActivityComponent {
void inject(ArticleView view);
}
Add to AppComponent:
Activitycomponent plusActivityComponent(ActivityModule activityModule);
ActivityComponentFactory
public final class ActivityComponentFactory {
public static ActivityComponent create(Activity activity) {
return ((NYTApp)activity.getApplicationContext).getComponent()
.plusActivityComponent(new ActivityModule(activity));
}
}
Activity Component Injection
public void onCreate(@Nullable Bundle savedInstanceState) {
activityComponent = ActivityComponentFactory.create(this);
activityComponent.inject(this);
Activity Component Modules
Activity Module
● Publish Subjects (mini bus)
● Reactive Text Resizer
● Snack Bar Util
Font Resizing
@Provides @ScopeActivity @FontBus
PublishSubject<Integer> provideFontChangeBus() {
return PublishSubject.create();
}
@Provides @ScopeActivity
FontResizer provideFontResize( @FontBus PublishSubject<Integer> fontBus) {
return new FontResizer(fontBus);
}
Usage of Font Resizer
@Inject
FontResizer fontResizer;
private void registerForFontResizing(View itemView) {
fontResizer.registerResize(itemView);
}
Usage of Font Resize “Bus”
@Inject
public SectionPresenter(@FontBus PublishSubject<Integer> fontBus) {
fontBus.subscribe(fontSize -> handleFontHasChanged());
}
Dagger helps us inject only what we need
SnackBarUtil
@ScopeActivity
public class SnackbarUtil {
@Inject Activity activity;
public Snackbar makeSnackbar(String txt, int duration) {
return Snackbar.make(...);}
}
….
In some presenter class:
public void onError(Throwable error) {
snackbarUtil.makeSnackbar(SaveHandler.SAVE_ERROR, SHORT).show();
}
Bundle Module, A Love Story
Bundle Management
Passing intent arguments to fragments/views is painful
● Need to save state
● Complexity with nested fragments
● Why we not inject intent arguments instead?
Create Bundle Service
public class BundleService {
private final Bundle data;
public BundleService(Bundle savedState, Bundle intentExtras) {
data = new Bundle();
if (savedState != null) {
data.putAll(savedState);
}
if (intentExtras != null) {
data.putAll(intentExtras);
}
}
Instantiate Bundle Service in Activity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
bundleService = new BundleService(savedInstanceState, getIntent().getExtras
());
//Never have to remember to save instance state again!
protected void onSaveInstanceState(Bundle outState) {
outState.putAll(bundleService.getAll());
Bind Bundle Service to Bundle Module
@Provides
@ScopeActivity
public BundleService provideBundleService(Activity context)
{
return ((Bundler) context).getBundleService();
}
Provide Individualized Intent Values
@Provides
@ScopeActivity
@AssetId
public Long provideArticleId(BundleService bundleService) {
return bundleService.get(ArticleActivity.ASSET_ID);
}
Inject Intent Values Directly into Views & Presenters
@Inject
public CommentPresenter(@AssetId String assetId){
//fetch comments for current article
}
Old Way
Normally we would have to pass assetId from:
ArticleActivity to
ArticleFragment to
CommentFragment to
CommentView to
CommentPresenter
:-l
Testing
Simple Testing
JUNIT Mockito, AssertJ
@Mock
AppPreferences prefs;
@Before public void setUp() {
inboxPref = new InboxPreferences(prefs);
}
@Test
public void testGetUserChannelPreferencesEmpty() {
when(prefs.getPreference(IUSER_CHANNELS,emptySet())) .thenReturn(null);
assertThat(inboxPref.getUserChannel()).isEmpty();
}
Testing with Dagger
Dagger BaseTestCase
public abstract class BaseTestCase extends TestCase {
protected TestComponent getTestComponent() {
final ApplicationModule applicationModule = getApplicationModule();
return Dagger2Helper.buildComponent(
TestComponent.class,
applicationModule));}
TestComponent
@Singleton
@Component(modules = {TestModule.class,ApplicationModule.class,
FlavorModule.class, TypeModule.class, AnalyticsModule.class, EcommModule.
class, PushModule.class})
public interface TestComponent {
void inject(WebViewUtilTest test);
Dagger Test with Mocks
public class WebViewUtilTest extends BaseTestCase {
@Inject NetworkStatus networkStatus;
@Inject WebViewUtil webViewUtil;
protected ApplicationModule getApplicationModule() {
return new ApplicationModule(application) {
protected NetworkStatus provideNetworkStatus() {
return mock(NetworkStatus.class);
}
};
}
Dagger Test with Mocks
public class WebViewUtilTest extends BaseTestCase {
@Inject NetworkStatus networkStatus;
@Inject WebViewUtil webViewUtil;
…
@Test
public void testNoValueOnOffline() throws Exception {
when(networkStatus.isInternetConnected()).thenReturn(false);
webViewUtil.getIntentLauncher().subscribe(intent -> {fail("intent was launched");});}
Dagger Test with Mocks Gotchas
● Must have provides method
● Must be in module you are explicitly passing into Dagger
Functional/Espresso Testing
NYTFunctionalTestApp
● Creates Component with overridden providers
NYTFunctionalTestApp
● Creates Component with overridden providers
● Mostly no-op since this is global
■ Analytics
■ AB Manager
■ Other Test impls (network, disk)
NYTFunctionalTestApp
● Creates Component with overridden providers
● Mostly no-op since this is global
■ Analytics
■ AB Manager
■ Other Test impls (network, disk)
● Functional test runner uses custom FunctTestApp
NYTFunctionalTestApp
● Creates Component with overridden providers
● Mostly no-op since this is global
■ Analytics
■ AB Manager
■ Other Test impls (network, disk)
● Functional test runner uses custom FunctTestApp
● Test run end to end otherwise
NYTFunctionalTestApp
public class NYTFunctionalTestsApp extends NYTApplication {
ComponentFactory componentFactory(Application context) {
return new ComponentFactory() {
protected DaggerApplicationComponent.Builder componentBuilder(Application context) {
return super.componentBuilder(context)
.applicationModule(new ApplicationModule(NYTFunctionalTestsApp.this) {
protected ABManager provideABManager() {
return new NoOpABManager();
}
NYTFunctionalTestRunner
public class NYTFunctionalTestsRunner extends AndroidJUnitRunner {
@Override
public Application newApplication(ClassLoader cl,String className, Context context)
{ return newApplication(NYTFunctionalTestsApp.class, context);
}
}
Sample Espresso Test
@RunWith(AndroidJUnit4.class)
public class MainScreenTests {
@Test
public void openMenuAndCheckItems() {
mainScreen
.openMenuDialog()
.assertMenuDialogContainsItemWithText(R.string.dialog_menu_font_resize)
.assertMenuDialogContainsItemWithText(R.string.action_settings);
}
@NYTDevs | developers.nytimes.com
Questions?
@NYTDevs | developers.nytimes.com
Thank You!
(We’re hiring)
@FriendlyMikhail

More Related Content

What's hot

Android application model
Android application modelAndroid application model
Android application modelmagicshui
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaFabio Collini
 
Dependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutesDependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutesAntonio Goncalves
 
A friend in need - A JS indeed
A friend in need - A JS indeedA friend in need - A JS indeed
A friend in need - A JS indeedYonatan Levin
 
Sharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SFSharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SFPierre-Yves Ricau
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKFabio Collini
 
Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6Ray Ploski
 
Антон Минашкин "Dagger 2. Right way to do Dependency Injections"
Антон Минашкин "Dagger 2. Right way to do Dependency Injections"Антон Минашкин "Dagger 2. Right way to do Dependency Injections"
Антон Минашкин "Dagger 2. Right way to do Dependency Injections"Fwdays
 
Using hilt in a modularized project
Using hilt in a modularized projectUsing hilt in a modularized project
Using hilt in a modularized projectFabio Collini
 
Android programming -_pushing_the_limits
Android programming -_pushing_the_limitsAndroid programming -_pushing_the_limits
Android programming -_pushing_the_limitsDroidcon Berlin
 
Deep dive into Android async operations
Deep dive into Android async operationsDeep dive into Android async operations
Deep dive into Android async operationsMateusz Grzechociński
 
Dagger 2 vs koin
Dagger 2 vs koinDagger 2 vs koin
Dagger 2 vs koinJintin Lin
 
React table tutorial use filter (part 2)
React table tutorial use filter (part 2)React table tutorial use filter (part 2)
React table tutorial use filter (part 2)Katy Slemon
 
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)Ontico
 
What's Coming in Spring 3.0
What's Coming in Spring 3.0What's Coming in Spring 3.0
What's Coming in Spring 3.0Matt Raible
 
Making React Native UI Components with Swift
Making React Native UI Components with SwiftMaking React Native UI Components with Swift
Making React Native UI Components with SwiftRay Deck
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaFabio Collini
 

What's hot (20)

Android application model
Android application modelAndroid application model
Android application model
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
 
Dependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutesDependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutes
 
A friend in need - A JS indeed
A friend in need - A JS indeedA friend in need - A JS indeed
A friend in need - A JS indeed
 
Sharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SFSharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SF
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
 
Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6
 
Антон Минашкин "Dagger 2. Right way to do Dependency Injections"
Антон Минашкин "Dagger 2. Right way to do Dependency Injections"Антон Минашкин "Dagger 2. Right way to do Dependency Injections"
Антон Минашкин "Dagger 2. Right way to do Dependency Injections"
 
Using hilt in a modularized project
Using hilt in a modularized projectUsing hilt in a modularized project
Using hilt in a modularized project
 
Android programming -_pushing_the_limits
Android programming -_pushing_the_limitsAndroid programming -_pushing_the_limits
Android programming -_pushing_the_limits
 
Vue next
Vue nextVue next
Vue next
 
Learn react-js
Learn react-jsLearn react-js
Learn react-js
 
Deep dive into Android async operations
Deep dive into Android async operationsDeep dive into Android async operations
Deep dive into Android async operations
 
Dagger 2 vs koin
Dagger 2 vs koinDagger 2 vs koin
Dagger 2 vs koin
 
React table tutorial use filter (part 2)
React table tutorial use filter (part 2)React table tutorial use filter (part 2)
React table tutorial use filter (part 2)
 
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
Паразитируем на React-экосистеме (Angular 4+) / Алексей Охрименко (IPONWEB)
 
What's Coming in Spring 3.0
What's Coming in Spring 3.0What's Coming in Spring 3.0
What's Coming in Spring 3.0
 
Making React Native UI Components with Swift
Making React Native UI Components with SwiftMaking React Native UI Components with Swift
Making React Native UI Components with Swift
 
Typescript barcelona
Typescript barcelonaTypescript barcelona
Typescript barcelona
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
 

Similar to Dagger Dependency Injection at NYT

Testing android apps with espresso
Testing android apps with espressoTesting android apps with espresso
Testing android apps with espressoÉdipo Souza
 
Google app-engine-with-python
Google app-engine-with-pythonGoogle app-engine-with-python
Google app-engine-with-pythonDeepak Garg
 
Dependency injection using dagger2
Dependency injection using dagger2Dependency injection using dagger2
Dependency injection using dagger2Javad Hashemi
 
Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Alessandro Molina
 
Getting started with appium
Getting started with appiumGetting started with appium
Getting started with appiumPratik Patel
 
Innovation Generation - The Mobile Meetup: Android Best Practices
Innovation Generation - The Mobile Meetup: Android Best PracticesInnovation Generation - The Mobile Meetup: Android Best Practices
Innovation Generation - The Mobile Meetup: Android Best PracticesSolstice Mobile Argentina
 
20180518 QNAP Seminar - Introduction to React Native
20180518 QNAP Seminar - Introduction to React Native20180518 QNAP Seminar - Introduction to React Native
20180518 QNAP Seminar - Introduction to React NativeEric Deng
 
North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017
North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017
North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017Ortus Solutions, Corp
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsJeff Durta
 
Introduction to React Native
Introduction to React NativeIntroduction to React Native
Introduction to React NativeDamian Zbrożek
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Nicolas HAAN
 
Android architecture
Android architecture Android architecture
Android architecture Trong-An Bui
 
Unit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdfUnit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdfKaty Slemon
 
Design patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsDesign patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsRavi Bhadauria
 
JS Fest 2018. Anna Herlihy. How to Write a Compass Plugin
JS Fest 2018. Anna Herlihy. How to Write a Compass PluginJS Fest 2018. Anna Herlihy. How to Write a Compass Plugin
JS Fest 2018. Anna Herlihy. How to Write a Compass PluginJSFestUA
 
3 WAYS TO TEST YOUR COLDFUSION API
3 WAYS TO TEST YOUR COLDFUSION API3 WAYS TO TEST YOUR COLDFUSION API
3 WAYS TO TEST YOUR COLDFUSION APIGavin Pickin
 
3 WAYS TO TEST YOUR COLDFUSION API -
3 WAYS TO TEST YOUR COLDFUSION API - 3 WAYS TO TEST YOUR COLDFUSION API -
3 WAYS TO TEST YOUR COLDFUSION API - Ortus Solutions, Corp
 
Writing Tests with the Unity Test Framework
Writing Tests with the Unity Test FrameworkWriting Tests with the Unity Test Framework
Writing Tests with the Unity Test FrameworkPeter Kofler
 
Fundamental concepts of react js
Fundamental concepts of react jsFundamental concepts of react js
Fundamental concepts of react jsStephieJohn
 

Similar to Dagger Dependency Injection at NYT (20)

Testing android apps with espresso
Testing android apps with espressoTesting android apps with espresso
Testing android apps with espresso
 
Google app-engine-with-python
Google app-engine-with-pythonGoogle app-engine-with-python
Google app-engine-with-python
 
Dependency injection using dagger2
Dependency injection using dagger2Dependency injection using dagger2
Dependency injection using dagger2
 
Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2Rapid Prototyping with TurboGears2
Rapid Prototyping with TurboGears2
 
Getting started with appium
Getting started with appiumGetting started with appium
Getting started with appium
 
Innovation Generation - The Mobile Meetup: Android Best Practices
Innovation Generation - The Mobile Meetup: Android Best PracticesInnovation Generation - The Mobile Meetup: Android Best Practices
Innovation Generation - The Mobile Meetup: Android Best Practices
 
20180518 QNAP Seminar - Introduction to React Native
20180518 QNAP Seminar - Introduction to React Native20180518 QNAP Seminar - Introduction to React Native
20180518 QNAP Seminar - Introduction to React Native
 
North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017
North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017
North Virginia Coldfusion User Group Meetup - Testbox - July 19th 2017
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
Introduction to React Native
Introduction to React NativeIntroduction to React Native
Introduction to React Native
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
 
Android architecture
Android architecture Android architecture
Android architecture
 
Unit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdfUnit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdf
 
Design patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjsDesign patterns in java script, jquery, angularjs
Design patterns in java script, jquery, angularjs
 
JS Fest 2018. Anna Herlihy. How to Write a Compass Plugin
JS Fest 2018. Anna Herlihy. How to Write a Compass PluginJS Fest 2018. Anna Herlihy. How to Write a Compass Plugin
JS Fest 2018. Anna Herlihy. How to Write a Compass Plugin
 
Frontend training
Frontend trainingFrontend training
Frontend training
 
3 WAYS TO TEST YOUR COLDFUSION API
3 WAYS TO TEST YOUR COLDFUSION API3 WAYS TO TEST YOUR COLDFUSION API
3 WAYS TO TEST YOUR COLDFUSION API
 
3 WAYS TO TEST YOUR COLDFUSION API -
3 WAYS TO TEST YOUR COLDFUSION API - 3 WAYS TO TEST YOUR COLDFUSION API -
3 WAYS TO TEST YOUR COLDFUSION API -
 
Writing Tests with the Unity Test Framework
Writing Tests with the Unity Test FrameworkWriting Tests with the Unity Test Framework
Writing Tests with the Unity Test Framework
 
Fundamental concepts of react js
Fundamental concepts of react jsFundamental concepts of react js
Fundamental concepts of react js
 

Recently uploaded

Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfkalichargn70th171
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Rob Geurden
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf31events.com
 
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdfInnovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdfYashikaSharma391629
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...Akihiro Suda
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZABSYZ Inc
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 

Recently uploaded (20)

Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf
 
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdfInnovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdf
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZ
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 

Dagger Dependency Injection at NYT

  • 1. @NYTDevs | developers.nytimes.com Mike Nakhimovich @FriendlyMikhail Android Framework Team Swordfighting with Dagger Dependecy Injection Made Less Simple
  • 2. What is Dagger? Alternative way to instantiate and manage your objects ● Guice - Google (Dagger v.0) ● Dagger 1 - Square ● Dagger 2 - Back to Google :-)
  • 3. Why Do We Need It? Good Code = Testable Code
  • 4. Why Do We Need It? More Tests = Less Anxiety
  • 5. Why Do We Need It? Proper Code Organization is a requirement for testing
  • 6. Untestable Code (Me in the Beginning) public class MyClass { private Model model; public MyClass() {this.model = new Model();} public String getName() {return model.getName();} } How can we test if model.getName() was called?
  • 7. Internet Told Me to Externalize My Dependencies public class MyClass { ... public MyClass(Model model) {this.model = model;} public String getName() {return model.getName();} }... public void testGetData(){ Model model = mock(Model.class); when(model.getName()).thenReturn("Mike"); MyClass myClass = new MyClass(model).getName(); verify(myClass.getName()).isEq("Mike"); }
  • 8. Where Does Model Come From? Dependency Injection to the rescue!
  • 9. Dagger Helps You Externalize Object Creation @Provides Model provideModel(){ return new Model(); }
  • 10. Provide from Modules @Module public class AppModule{ } A module is a part of your application that provides some functionality.
  • 11. Provide from Modules @Module public class AppModule{ @Provides Model provideModel(){ return new Model(); } ... A module is a part of your application that provides some functionality.
  • 12. Components are Composed of Modules @Singleton @Component(modules = {MyModule.class}) public interface AppComponent { void inject(MyActivity activity); } A Component is the manager of all your module providers
  • 13. Next, Create a Component Instance component = DaggerAppComponent.builder() .myModule(new MyModule(this)) .build();
  • 14. Register with Component protected void onCreate(Bundle savedInstanceState) { getApplicationComponent().inject(this);
  • 15. Injection Fun Now you can inject dependencies as fields or constructor arguments @Inject Model model; @Inject public Presenter(Model model)
  • 16. Dagger @ NY Times Now for the fun stuff! 50% Recipes 50% Ramblings
  • 17. Dagger @ NY Times ● Module/Component Architecture ○ Working with libraries ○ Build Types & Flavors ● Scopes ○ Application ○ Activity (Now with Singletons!) ● Testing ○ Espresso ○ Unit Testing
  • 18. Code Organization How Dagger manages 6 build variants & 6+ libraries GoogleDebug AmazonDebug GoogleBetaAmazonBeta GoogleRelease AmazonRelease
  • 19. Application Scoped Modules ● App Module ● Build Type Module ● Library Module ● Flavor Module
  • 20. App Module Singletons ○ Parser (configured GSON)
  • 21. App Module Singletons ○ Parser (configured GSON) ○ IO Managers
  • 22. App Module Singletons ○ Parser (configured GSON) ○ IO Managers ○ Configs (Analytics,AB, E-Commerce)
  • 23. Example Library Module: E-Commerce @Module public class ECommModule { @Provides @Singleton public ECommBuilder provideECommBuilder( )
  • 24. E-Comm using App Module’s Dep @Module public class ECommModule { @Provides @Singleton public ECommBuilder provideECommBuilder(ECommConfig config){ return new ECommManagerBuilder().setConfig(config); }
  • 25. Amazon & Google Flavors ● Amazon Variants needs Amazon E-Commerce ● Google Variants needs to contain Google E-Commerce How can Dagger help?
  • 26. E-Comm Qualified Provider @Module public class ECommModule { @Provides @Singleton public ECommBuilder provideECommBuilder(ECommConfig config){ return new ECommManagerBuilder().setConfig(config); } @Provides @Singleton @Google public ECommManager providesGoogleEComm (ECommBuilder builder, GooglePayments googlePayments)
  • 27. E-Comm Qualified Provider @Module public class ECommModule { @Provides @Singleton public ECommBuilder provideECommBuilder(ECommConfig config){ return new ECommManagerBuilder().setConfig(config); } ... @Provides @Singleton @Amazon public ECommManager providesAmazonEComm (ECommBuilder builder, AmazonPayments amazonPayments)
  • 28. Flavor Module: src/google & src/Amazon @Module public class FlavorModule { }
  • 29. Flavor Module Provides Non-Qualified E-Comm @Module public class FlavorModule { @Singleton @Provides ECommManager provideECommManager(@Google ECommManager ecomm) } Note: Proguard strips out the other impl from Jar :-)
  • 30. Type Module Brings build specific dependencies/providers in Type Module
  • 31. Type Module Brings build specific dependencies/providers in Type Module ○ Logging ■ Most logging for Beta Build ■ No-Op Release
  • 32. Type Module Brings build specific dependencies/providers in Type Module ○ Logging ■ Most logging for Beta Build ■ No-Op Release ○ Payments ■ No-Op for debug
  • 33. Type Module ● Brings build specific dependencies/providers in Type Module ○ Logging ■ Most logging for Beta Build ■ No-Op Release ○ Payments ■ No-Op for debug ○ Device ID ■ Static for Debug
  • 34. Component Composition How we combine our modules
  • 35. Start with Base Component ● Base Component lives in src/main ● Contains inject(T t) for classes & Services that register with Dagger (non flavor/build specific) interface BaseComponent { void inject(NYTApplication target); }
  • 36. Src/Google & Src/Amazon Contain a FlavorComponent ● Create FlavorComponent that inherits from BaseComponent ● Register inject for flavor specific classes ● Anything not in src/flavor that needs component registers here ie: ○ Messaging Service ○ Payment Activity public interface FlavorComponent extends BaseComponent { void inject(ADMessaging target); }
  • 37. App Component debug, beta, release One for src/debug src/beta src/release public interface ApplicationComponent { }
  • 38. App Component Inherits from Flavor Component public interface ApplicationComponent extends FlavorComponent { }
  • 39. App Component ● Adds @Component @Singleton annotations @Singleton @Component public interface ApplicationComponent extends FlavorComponent { }
  • 40. App Component ● Adds modules @Singleton @Component(modules = {ApplicationModule.class, FlavorModule.class, TypeModule.class, AnalyticsModule.class, ECommModule.class, PushClientModule.class }) public interface ApplicationComponent extends FlavorComponent { }
  • 41. Anything registering with App Component gains access to all providers for the Flavor/Type
  • 42. Usage of Generated App Component
  • 43. App Component Factory public class ComponentFactory { public AppComponent buildComponent(Application context) { return componentBuilder(context).build(); } // We override it for functional tests. DaggerApplicationComponent.Builder componentBuilder(Application context) { return DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule(context)} }
  • 44. Component Instance ● NYT Application retains component private void buildComponentAndInject() { appComponent = componentFactory().buildComponent(this); appComponent.inject(this); } public ComponentFactory componentFactory() { return new ComponentFactory(); }
  • 46. Activity Component ● Inherits all “provides” from App Component ● Allows you to add “Activity Singletons” ○ 1 Per Activity ○ Many views/fragments within activity can inject same instance
  • 47. ActivityComponent @Subcomponent(modules = {ActivityModule.class, BundleModule.class}) @ScopeActivity public interface ActivityComponent { void inject(ArticleView view); } Add to AppComponent: Activitycomponent plusActivityComponent(ActivityModule activityModule);
  • 48. ActivityComponentFactory public final class ActivityComponentFactory { public static ActivityComponent create(Activity activity) { return ((NYTApp)activity.getApplicationContext).getComponent() .plusActivityComponent(new ActivityModule(activity)); } }
  • 49. Activity Component Injection public void onCreate(@Nullable Bundle savedInstanceState) { activityComponent = ActivityComponentFactory.create(this); activityComponent.inject(this);
  • 51. Activity Module ● Publish Subjects (mini bus) ● Reactive Text Resizer ● Snack Bar Util
  • 52. Font Resizing @Provides @ScopeActivity @FontBus PublishSubject<Integer> provideFontChangeBus() { return PublishSubject.create(); } @Provides @ScopeActivity FontResizer provideFontResize( @FontBus PublishSubject<Integer> fontBus) { return new FontResizer(fontBus); }
  • 53. Usage of Font Resizer @Inject FontResizer fontResizer; private void registerForFontResizing(View itemView) { fontResizer.registerResize(itemView); }
  • 54. Usage of Font Resize “Bus” @Inject public SectionPresenter(@FontBus PublishSubject<Integer> fontBus) { fontBus.subscribe(fontSize -> handleFontHasChanged()); } Dagger helps us inject only what we need
  • 55. SnackBarUtil @ScopeActivity public class SnackbarUtil { @Inject Activity activity; public Snackbar makeSnackbar(String txt, int duration) { return Snackbar.make(...);} } …. In some presenter class: public void onError(Throwable error) { snackbarUtil.makeSnackbar(SaveHandler.SAVE_ERROR, SHORT).show(); }
  • 56. Bundle Module, A Love Story
  • 57. Bundle Management Passing intent arguments to fragments/views is painful ● Need to save state ● Complexity with nested fragments ● Why we not inject intent arguments instead?
  • 58. Create Bundle Service public class BundleService { private final Bundle data; public BundleService(Bundle savedState, Bundle intentExtras) { data = new Bundle(); if (savedState != null) { data.putAll(savedState); } if (intentExtras != null) { data.putAll(intentExtras); } }
  • 59. Instantiate Bundle Service in Activity @Override protected void onCreate(@Nullable Bundle savedInstanceState) { bundleService = new BundleService(savedInstanceState, getIntent().getExtras ()); //Never have to remember to save instance state again! protected void onSaveInstanceState(Bundle outState) { outState.putAll(bundleService.getAll());
  • 60. Bind Bundle Service to Bundle Module @Provides @ScopeActivity public BundleService provideBundleService(Activity context) { return ((Bundler) context).getBundleService(); }
  • 61. Provide Individualized Intent Values @Provides @ScopeActivity @AssetId public Long provideArticleId(BundleService bundleService) { return bundleService.get(ArticleActivity.ASSET_ID); }
  • 62. Inject Intent Values Directly into Views & Presenters @Inject public CommentPresenter(@AssetId String assetId){ //fetch comments for current article }
  • 63. Old Way Normally we would have to pass assetId from: ArticleActivity to ArticleFragment to CommentFragment to CommentView to CommentPresenter :-l
  • 65. Simple Testing JUNIT Mockito, AssertJ @Mock AppPreferences prefs; @Before public void setUp() { inboxPref = new InboxPreferences(prefs); } @Test public void testGetUserChannelPreferencesEmpty() { when(prefs.getPreference(IUSER_CHANNELS,emptySet())) .thenReturn(null); assertThat(inboxPref.getUserChannel()).isEmpty(); }
  • 67. Dagger BaseTestCase public abstract class BaseTestCase extends TestCase { protected TestComponent getTestComponent() { final ApplicationModule applicationModule = getApplicationModule(); return Dagger2Helper.buildComponent( TestComponent.class, applicationModule));}
  • 68. TestComponent @Singleton @Component(modules = {TestModule.class,ApplicationModule.class, FlavorModule.class, TypeModule.class, AnalyticsModule.class, EcommModule. class, PushModule.class}) public interface TestComponent { void inject(WebViewUtilTest test);
  • 69. Dagger Test with Mocks public class WebViewUtilTest extends BaseTestCase { @Inject NetworkStatus networkStatus; @Inject WebViewUtil webViewUtil; protected ApplicationModule getApplicationModule() { return new ApplicationModule(application) { protected NetworkStatus provideNetworkStatus() { return mock(NetworkStatus.class); } }; }
  • 70. Dagger Test with Mocks public class WebViewUtilTest extends BaseTestCase { @Inject NetworkStatus networkStatus; @Inject WebViewUtil webViewUtil; … @Test public void testNoValueOnOffline() throws Exception { when(networkStatus.isInternetConnected()).thenReturn(false); webViewUtil.getIntentLauncher().subscribe(intent -> {fail("intent was launched");});}
  • 71. Dagger Test with Mocks Gotchas ● Must have provides method ● Must be in module you are explicitly passing into Dagger
  • 73. NYTFunctionalTestApp ● Creates Component with overridden providers
  • 74. NYTFunctionalTestApp ● Creates Component with overridden providers ● Mostly no-op since this is global ■ Analytics ■ AB Manager ■ Other Test impls (network, disk)
  • 75. NYTFunctionalTestApp ● Creates Component with overridden providers ● Mostly no-op since this is global ■ Analytics ■ AB Manager ■ Other Test impls (network, disk) ● Functional test runner uses custom FunctTestApp
  • 76. NYTFunctionalTestApp ● Creates Component with overridden providers ● Mostly no-op since this is global ■ Analytics ■ AB Manager ■ Other Test impls (network, disk) ● Functional test runner uses custom FunctTestApp ● Test run end to end otherwise
  • 77. NYTFunctionalTestApp public class NYTFunctionalTestsApp extends NYTApplication { ComponentFactory componentFactory(Application context) { return new ComponentFactory() { protected DaggerApplicationComponent.Builder componentBuilder(Application context) { return super.componentBuilder(context) .applicationModule(new ApplicationModule(NYTFunctionalTestsApp.this) { protected ABManager provideABManager() { return new NoOpABManager(); }
  • 78. NYTFunctionalTestRunner public class NYTFunctionalTestsRunner extends AndroidJUnitRunner { @Override public Application newApplication(ClassLoader cl,String className, Context context) { return newApplication(NYTFunctionalTestsApp.class, context); } }
  • 79. Sample Espresso Test @RunWith(AndroidJUnit4.class) public class MainScreenTests { @Test public void openMenuAndCheckItems() { mainScreen .openMenuDialog() .assertMenuDialogContainsItemWithText(R.string.dialog_menu_font_resize) .assertMenuDialogContainsItemWithText(R.string.action_settings); }
  • 81. @NYTDevs | developers.nytimes.com Thank You! (We’re hiring) @FriendlyMikhail