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

C716975ef0b4ed35553572ff44ea1bed?s=128

Daniel Molinero Reguera

April 11, 2017
Tweet

Transcript

  1. A FRESH APPROACH TO DI TOOTHPICK https://github.com/stephanenicolas/toothpick/

  2. 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
  3. 5IF"OESPJE5FBNJT)JSJOH
 jobs.groupon.com/careers/

  4. 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
  5. WHY DI ? #DroidConBos | @D_Lemures

  6. WHY DI ? DECOUPLE #DroidConBos | @D_Lemures

  7. WHY DI ? DECOUPLE USES #DroidConBos | @D_Lemures

  8. WHY DI ? DECOUPLE REUSE USES #DroidConBos | @D_Lemures

  9. WHY DI ? DECOUPLE REUSE USES USES USES #DroidConBos |

    @D_Lemures
  10. WHY DI ? DECOUPLE REUSE TEST USES USES USES #DroidConBos

    | @D_Lemures
  11. WHY DI ? DECOUPLE REUSE TEST USES USES USES MOCK

    USES #DroidConBos | @D_Lemures
  12. WHY TOOTHPICK? #DroidConBos | @D_Lemures

  13. WHY TOOTHPICK? SIMPLER #DroidConBos | @D_Lemures

  14. WHY TOOTHPICK? SIMPLER BETTER TEST SUPPORT #DroidConBos | @D_Lemures

  15. WHY TOOTHPICK? SIMPLER BETTER TEST SUPPORT EVEN FASTER #DroidConBos |

    @D_Lemures
  16. 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
  17. 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
  18. TOOTHPICK FEATURES SCOPE SCOPE #DroidConBos | @D_Lemures

  19. TOOTHPICK FEATURES SCOPE SCOPE MAKE INJECTIONS #DroidConBos | @D_Lemures

  20. 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
  21. TOOTHPICK FEATURES SCOPE, MODULES & BINDINGS SCOPE #DroidConBos | @D_Lemures

  22. TOOTHPICK FEATURES SCOPE, MODULES & BINDINGS SCOPE BINDINGS #DroidConBos |

    @D_Lemures
  23. TOOTHPICK FEATURES SCOPE, MODULES & BINDINGS SCOPE BINDINGS MODULES #DroidConBos

    | @D_Lemures
  24. 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
  25. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY 1
 SCOPE SCOPE TREE #DroidConBos

    | @D_Lemures
  26. TOOTHPICK FEATURES APPLICATION
 SCOPE SCOPE TREE #DroidConBos | @D_Lemures

  27. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY 2
 SCOPE SCOPE TREE #DroidConBos

    | @D_Lemures
  28. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY 2
 SCOPE SERVICE 1
 SCOPE

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

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

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

    FRAGMENT 3
 SCOPE FRAGMENT 1
 SCOPE FRAGMENT 2
 SCOPE SCOPE TREE #DroidConBos | @D_Lemures
  32. 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
  33. 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
  34. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY
 SCOPE SCOPE SINGLETONS #DroidConBos |

    @D_Lemures
  35. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY
 SCOPE APP SINGLETONS SCOPE SINGLETONS

    #DroidConBos | @D_Lemures
  36. TOOTHPICK FEATURES APPLICATION
 SCOPE ACTIVITY
 SCOPE APP SINGLETONS ACTIVITY SINGLETONS

    SCOPE SINGLETONS #DroidConBos | @D_Lemures
  37. 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
  38. TOOTHPICK FEATURES @Singleton
 public class IceMachine {
 } @ActivitySingleton
 public

    class SmoothieMachine {
 } APPLICATION
 SCOPE ACTIVITY
 SCOPE SCOPE SINGLETONS OR USING ANNOTATIONS #DroidConBos | @D_Lemures
  39. SOME EXAMPLES MVP & STATE PRESERVATION #DroidConBos | @D_Lemures

  40. APPLICATION SCOPE MVP & STATE PRESERVATION ACTIVITY SCOPE SOME EXAMPLES

    #DroidConBos | @D_Lemures
  41. APPLICATION SCOPE MVP & STATE PRESERVATION ACTIVITY SCOPE SOME EXAMPLES

    #DroidConBos | @D_Lemures
  42. APPLICATION SCOPE PRESENTER SCOPE MVP & STATE PRESERVATION ACTIVITY SCOPE

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

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

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

    ACTIVITY SCOPE SOME EXAMPLES #DroidConBos | @D_Lemures
  46. 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
  47. @PresenterSingleton
 public class MyPresenter {
 String dealId;
 int quantity;
 }

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

    EXAMPLES #DroidConBos | @D_Lemures
  49. ACTIVITY 1 ACTIVITY 3 ACTIVITY 2 MULTI ACTIVITY FLOWS Purchase

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

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

    EXAMPLES #DroidConBos | @D_Lemures
  52. ACTIVITY 1 ACTIVITY 3 ACTIVITY 2 MULTI ACTIVITY FLOWS SOME

    EXAMPLES #DroidConBos | @D_Lemures
  53. ACTIVITY 1 ACTIVITY 3 ACTIVITY 2 MULTI ACTIVITY FLOWS SOME

    EXAMPLES #DroidConBos | @D_Lemures
  54. ACTIVITY 1 ACTIVITY 3 ACTIVITY 2 MULTI ACTIVITY FLOWS SOME

    EXAMPLES #DroidConBos | @D_Lemures
  55. APPLICATION SCOPE ACTIVITY 1 SCOPE FLOW SCOPE MULTI ACTIVITY FLOWS

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

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

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

    ACTIVITY 2 SCOPE MULTI ACTIVITY FLOWS SOME EXAMPLES #DroidConBos | @D_Lemures
  59. APPLICATION SCOPE FLOW SCOPE MULTI ACTIVITY FLOWS SOME EXAMPLES #DroidConBos

    | @D_Lemures
  60. APPLICATION SCOPE MULTI ACTIVITY FLOWS SOME EXAMPLES #DroidConBos | @D_Lemures

  61. 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
  62. @FlowSingleton
 public class ShoppingCart {
 List<PurchaseItem> purchases…
 } FLOW SCOPE

    MULTI ACTIVITY FLOWS SOME EXAMPLES #DroidConBos | @D_Lemures
  63. UNIT TESTING WE TEST UNITS #DroidConBos | @D_Lemures

  64. UNIT TESTING WE TEST UNITS MOCK USES WE MOCK DEPENDENCIES

    #DroidConBos | @D_Lemures
  65. 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
  66. 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
  67. 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
  68. CONCLUSION QUESTIONS ? COMMENTS ? #DroidConBos | @D_Lemures | @GrouponEng

  69. CONCLUSION QUESTIONS ? COMMENTS ? #DroidConBos | @D_Lemures | @GrouponEng

  70. A FRESH APPROACH TO DI TOOTHPICK https://github.com/stephanenicolas/toothpick/ +stephane nicolas @D_Lemures