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

Toothpick - A fresh approach to DI (Including Unit Testing)

Toothpick - A fresh approach to DI (Including Unit Testing)

Presented at Droidcon Boston 2017

Daniel Molinero Reguera

April 11, 2017
Tweet

More Decks by Daniel Molinero Reguera

Other Decks in Programming

Transcript

  1. ABOUT US Senior Android Dev @ Groupon
 ~20 years of

    Java coding OSS: Dart, TP, BoundBox, … STEPHANE NICOLAS DANIEL MOLINERO REGUERA Android Dev @ Groupon
 Turbulence Ninja OSS: Dart, TP. +stephane nicolas @D_Lemures
  2. THE TALK WHY DI ? WHY TOOTHPICK? TOOTHPICK FEATURES 


    SOME EXAMPLES 
 
 UNIT TESTING CONCLUSION SCOPE MODULES & BINDINGS SCOPE TREE SCOPE SINGLETONS MVP & STATE PRESERVATION MULTIPLE ACTIVITIES FLOWS #DroidConBos | @D_Lemures | @GrouponEng
  3. WHY DI ? DECOUPLE REUSE TEST USES USES USES MOCK

    USES #DroidConBos | @D_Lemures
  4. TOOTHPICK FEATURES public class SmoothieMachine {
 
 @Inject IceMachine iceMachine;


    
 public void doSmoothie() {
 iceMachine.makeIce();
 }
 } @Singleton
 public class IceMachine {
 Foo foo;
 
 @Inject
 public IceMachine(Foo foo) {
 this.foo = foo;
 }
 #DroidConBos | @D_Lemures
  5. TOOTHPICK FEATURES public class SmoothieMachine {
 
 @Inject IceMachine iceMachine;


    
 public void doSmoothie() {
 iceMachine.makeIce();
 }
 } @Singleton
 public class IceMachine {
 Foo foo;
 
 @Inject
 public IceMachine(Foo foo) {
 this.foo = foo;
 }
 JSR 330 annotations→nothing new here #DroidConBos | @D_Lemures
  6. TOOTHPICK FEATURES SCOPE SCOPE MAKE INJECTIONS public class MyApplication extends

    Application { @Inject Machine machine;
 
 @Override protected void onCreate() {
 super.onCreate();
 
 Scope appScope = Toothpick.openScope(this); appScope.installModules(…); Toothpick.inject(this, appScope);
 } } #DroidConBos | @D_Lemures
  7. TOOTHPICK FEATURES SCOPE, MODULES & BINDINGS SCOPE BINDINGS MODULES public

    class MyApplication extends Application { @Inject Machine machine;
 
 @Override protected void onCreate() {
 super.onCreate();
 
 Scope appScope = Toothpick.openScope(this); appScope.installModules(new Module() {{
 bind(Machine.class).to(IceMachine.class); }});
 Toothpick.inject(this, appScope);
 } } #DroidConBos | @D_Lemures
  8. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY 2
 SCOPE SERVICE 1
 SCOPE

    FRAGMENT 1
 SCOPE SCOPE TREE #DroidConBos | @D_Lemures
  9. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY 2
 SCOPE SERVICE 1
 SCOPE

    FRAGMENT 1
 SCOPE FRAGMENT 2
 SCOPE SCOPE TREE #DroidConBos | @D_Lemures
  10. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY 2
 SCOPE SERVICE 1
 SCOPE

    FRAGMENT 3
 SCOPE FRAGMENT 1
 SCOPE FRAGMENT 2
 SCOPE SCOPE TREE #DroidConBos | @D_Lemures
  11. TOOTHPICK FEATURES public class LemonActivity extends Activity { @Inject Machine

    machine; @Inject Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScopes(getApplication(), this); scope.installModules(new Module() {{ bind(Context.class).toInstance(this); }}); Toothpick.inject(this, scope); } } SCOPE TREE #DroidConBos | @D_Lemures
  12. TOOTHPICK FEATURES public class LemonActivity extends Activity { @Inject Machine

    machine; @Inject Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Scope scope = Toothpick.openScopes(getApplication(), this); scope.installModules(new Module() {{ bind(Context.class).toInstance(this); }}); Toothpick.inject(this, scope); } } SCOPE TREE APPLICATION
 SCOPE ACTIVITY
 SCOPE #DroidConBos | @D_Lemures
  13. TOOTHPICK FEATURES public class Module extends Module {
 public Module()

    {
 bind(Machine.class).toInstance(new Ice Machine());
 }
 } SCOPE SINGLETONS SINGLETONS CAN BE DEFINED IN MODULES #DroidConBos | @D_Lemures
  14. TOOTHPICK FEATURES @Singleton
 public class IceMachine {
 } @ActivitySingleton
 public

    class SmoothieMachine {
 } APPLICATION
 SCOPE ACTIVITY
 SCOPE SCOPE SINGLETONS OR USING ANNOTATIONS #DroidConBos | @D_Lemures
  15. PRESENTER INSTANCE APPLICATION SCOPE PRESENTER SCOPE MVP & STATE PRESERVATION

    ACTIVITY SCOPE SOME EXAMPLES #DroidConBos | @D_Lemures
  16. PRESENTER INSTANCE APPLICATION SCOPE PRESENTER SCOPE MVP & STATE PRESERVATION

    ACTIVITY SCOPE SOME EXAMPLES #DroidConBos | @D_Lemures
  17. MVP & STATE PRESERVATION public class MyActivity extends Activity {


    
 @Inject MyPresenter presenter; 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 Scope scope = openScopes(getApplication(), 
 PresenterSingleton.class, 
 this);
 Toothpick.inject(this, scope);
 } } SOME EXAMPLES #DroidConBos | @D_Lemures
  18. @PresenterSingleton
 public class MyPresenter {
 String dealId;
 int quantity;
 }

    PRESENTER SCOPE MVP & STATE PRESERVATION SOME EXAMPLES #DroidConBos | @D_Lemures
  19. ACTIVITY 1 ACTIVITY 3 ACTIVITY 2 MULTI ACTIVITY FLOWS Purchase

    Flow SOME EXAMPLES #DroidConBos | @D_Lemures
  20. ACTIVITY 1 ACTIVITY 3 ACTIVITY 2 MULTI ACTIVITY FLOWS Purchase

    Flow SOME EXAMPLES #DroidConBos | @D_Lemures
  21. APPLICATION SCOPE ACTIVITY 1 SCOPE FLOW SCOPE ACTIVITY 2 SCOPE

    MULTI ACTIVITY FLOWS SOME EXAMPLES #DroidConBos | @D_Lemures
  22. APPLICATION SCOPE ACTIVITY 1 SCOPE FLOW SCOPE ACTIVITY 3 SCOPE

    ACTIVITY 2 SCOPE MULTI ACTIVITY FLOWS SOME EXAMPLES #DroidConBos | @D_Lemures
  23. MVP & STATE PRESERVATION public class MyActivity extends Activity {


    
 @Inject ShoppingCart shoppingCart; 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 Scope scope = openScopes(getApplication(), 
 FlowSingleton.class, 
 this);
 Toothpick.inject(this, scope);
 } } SOME EXAMPLES #DroidConBos | @D_Lemures
  24. UNIT TESTING public class SmoothieMachine { @Inject IceMachine iceMachine; @Inject

    SyrupDispenser syrupDispenser; public void doSmoothie() { Smoothie smoothie = iceMachine.makeIce(); syrupDispenser.addSyrup(smoothie); return smoothie; } } WE WANT TO SIMULATE THE DEPENDENCIES #DroidConBos | @D_Lemures
  25. UNIT TESTING public class TestSmoothieMachine extends EasyMockSupport { @Rule public

    EasyMockRule easyMockRule = new EasyMockRule(this);
 
 @Mock IceMachine iceMachineMock; @Mock SyrupDispenser syrupDispenserMock; @Mock Smoothie smoothieMock;
 @Test
 public void doSmoothie_should_createSmoothie() {
 // GIVEN expect(iceMachineMock.makeIce()).andReturn(smoothieMock); syrupDispenserMock.addSyrup(smoothieMock); replayAll(); // WHEN Smoothie smoothie = smoothieMachine.doSmoothie(); // THEN assertThat(smoothie, sameInstance(smoothieMock)); verifyAll(); }
 } 1. CREATE MOCKS -> EASYMOCK #DroidConBos | @D_Lemures
  26. UNIT TESTING public class TestSmoothieMachine extends EasyMockSupport { @Rule public

    EasyMockRule easyMockRule = new EasyMockRule(this); @Rule public ToothPickRule toothPickRule = new ToothPickRule(this, “testScope”);
 
 @Mock IceMachine iceMachineMock; @Mock SyrupDispenser syrupDispenserMock; @Mock Smoothie smoothieMock; @Inject SmoothieMachine classUnderTest;
 @Test
 public void doSmoothie_should_createSmoothie() {
 // GIVEN expect(iceMachineMock.makeIce()).andReturn(smoothieMock); syrupDispenserMock.addSyrup(smoothieMock); replayAll(); // WHEN toothPickRule.inject(this); Smoothie smoothie = smoothieMachine.doSmoothie(); // THEN assertThat(smoothie, sameInstance(smoothieMock)); verifyAll(); }
 } 2. INJECT MOCKS -> TOOTHPICK #DroidConBos | @D_Lemures