A FRESH APPROACH TO DI
TOOTHPICK
https://github.com/stephanenicolas/toothpick/
Slide 2
Slide 2 text
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
Slide 3
Slide 3 text
5IF"OESPJE5FBNJT)JSJOH
jobs.groupon.com/careers/
Slide 4
Slide 4 text
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
Slide 5
Slide 5 text
WHY DI ? #DroidConBos | @D_Lemures
Slide 6
Slide 6 text
WHY DI ?
DECOUPLE
#DroidConBos | @D_Lemures
Slide 7
Slide 7 text
WHY DI ?
DECOUPLE USES
#DroidConBos | @D_Lemures
Slide 8
Slide 8 text
WHY DI ?
DECOUPLE
REUSE
USES
#DroidConBos | @D_Lemures
WHY DI ?
DECOUPLE
REUSE
TEST
USES
USES
USES
#DroidConBos | @D_Lemures
Slide 11
Slide 11 text
WHY DI ?
DECOUPLE
REUSE
TEST
USES
USES
USES
MOCK
USES
#DroidConBos | @D_Lemures
Slide 12
Slide 12 text
WHY TOOTHPICK? #DroidConBos | @D_Lemures
Slide 13
Slide 13 text
WHY TOOTHPICK?
SIMPLER
#DroidConBos | @D_Lemures
Slide 14
Slide 14 text
WHY TOOTHPICK?
SIMPLER
BETTER TEST SUPPORT
#DroidConBos | @D_Lemures
Slide 15
Slide 15 text
WHY TOOTHPICK?
SIMPLER
BETTER TEST SUPPORT
EVEN FASTER
#DroidConBos | @D_Lemures
Slide 16
Slide 16 text
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
Slide 17
Slide 17 text
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
Slide 18
Slide 18 text
TOOTHPICK FEATURES
SCOPE
SCOPE
#DroidConBos | @D_Lemures
Slide 19
Slide 19 text
TOOTHPICK FEATURES
SCOPE
SCOPE MAKE INJECTIONS
#DroidConBos | @D_Lemures
Slide 20
Slide 20 text
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
Slide 21
Slide 21 text
TOOTHPICK FEATURES
SCOPE, MODULES & BINDINGS
SCOPE
#DroidConBos | @D_Lemures
TOOTHPICK FEATURES
APPLICATION
SCOPE
ACTIVITY 1
SCOPE
SCOPE TREE
#DroidConBos | @D_Lemures
Slide 26
Slide 26 text
TOOTHPICK FEATURES
APPLICATION
SCOPE
SCOPE TREE
#DroidConBos | @D_Lemures
Slide 27
Slide 27 text
TOOTHPICK FEATURES
APPLICATION
SCOPE
ACTIVITY 2
SCOPE
SCOPE TREE
#DroidConBos | @D_Lemures
Slide 28
Slide 28 text
TOOTHPICK FEATURES
APPLICATION
SCOPE
ACTIVITY 2
SCOPE
SERVICE 1
SCOPE
SCOPE TREE
#DroidConBos | @D_Lemures
Slide 29
Slide 29 text
TOOTHPICK FEATURES
APPLICATION
SCOPE
ACTIVITY 2
SCOPE
SERVICE 1
SCOPE
FRAGMENT 1
SCOPE
SCOPE TREE
#DroidConBos | @D_Lemures
Slide 30
Slide 30 text
TOOTHPICK FEATURES
APPLICATION
SCOPE
ACTIVITY 2
SCOPE
SERVICE 1
SCOPE
FRAGMENT 1
SCOPE
FRAGMENT 2
SCOPE
SCOPE TREE
#DroidConBos | @D_Lemures
Slide 31
Slide 31 text
TOOTHPICK FEATURES
APPLICATION
SCOPE
ACTIVITY 2
SCOPE
SERVICE 1
SCOPE
FRAGMENT 3
SCOPE
FRAGMENT 1
SCOPE
FRAGMENT 2
SCOPE
SCOPE TREE
#DroidConBos | @D_Lemures
Slide 32
Slide 32 text
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
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
Slide 38
Slide 38 text
TOOTHPICK FEATURES
@Singleton
public class IceMachine {
}
@ActivitySingleton
public class SmoothieMachine {
}
APPLICATION
SCOPE
ACTIVITY
SCOPE
SCOPE SINGLETONS
OR USING ANNOTATIONS
#DroidConBos | @D_Lemures
Slide 39
Slide 39 text
SOME EXAMPLES
MVP & STATE PRESERVATION
#DroidConBos | @D_Lemures
Slide 40
Slide 40 text
APPLICATION
SCOPE
MVP & STATE PRESERVATION
ACTIVITY
SCOPE
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 41
Slide 41 text
APPLICATION
SCOPE
MVP & STATE PRESERVATION
ACTIVITY
SCOPE
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 42
Slide 42 text
APPLICATION
SCOPE
PRESENTER
SCOPE
MVP & STATE PRESERVATION
ACTIVITY
SCOPE
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 43
Slide 43 text
PRESENTER INSTANCE
APPLICATION
SCOPE
PRESENTER
SCOPE
MVP & STATE PRESERVATION
ACTIVITY
SCOPE
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 44
Slide 44 text
PRESENTER INSTANCE
APPLICATION
SCOPE
PRESENTER
SCOPE
MVP & STATE PRESERVATION
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 45
Slide 45 text
PRESENTER INSTANCE
APPLICATION
SCOPE
PRESENTER
SCOPE
MVP & STATE PRESERVATION
ACTIVITY
SCOPE
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 46
Slide 46 text
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
Slide 47
Slide 47 text
@PresenterSingleton
public class MyPresenter {
String dealId;
int quantity;
}
PRESENTER
SCOPE
MVP & STATE PRESERVATION
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 48
Slide 48 text
ACTIVITY 1 ACTIVITY 3
ACTIVITY 2
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 49
Slide 49 text
ACTIVITY 1 ACTIVITY 3
ACTIVITY 2
MULTI ACTIVITY FLOWS
Purchase Flow
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 50
Slide 50 text
ACTIVITY 1 ACTIVITY 3
ACTIVITY 2
MULTI ACTIVITY FLOWS
Purchase Flow
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 51
Slide 51 text
ACTIVITY 1 ACTIVITY 3
ACTIVITY 2
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 52
Slide 52 text
ACTIVITY 1 ACTIVITY 3
ACTIVITY 2
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 53
Slide 53 text
ACTIVITY 1 ACTIVITY 3
ACTIVITY 2
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 54
Slide 54 text
ACTIVITY 1 ACTIVITY 3
ACTIVITY 2
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 55
Slide 55 text
APPLICATION
SCOPE
ACTIVITY 1
SCOPE
FLOW SCOPE
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 56
Slide 56 text
APPLICATION
SCOPE
ACTIVITY 1
SCOPE
FLOW SCOPE
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 57
Slide 57 text
APPLICATION
SCOPE
ACTIVITY 1
SCOPE
FLOW SCOPE
ACTIVITY 2
SCOPE
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
APPLICATION
SCOPE
FLOW SCOPE
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 60
Slide 60 text
APPLICATION
SCOPE
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 61
Slide 61 text
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
Slide 62
Slide 62 text
@FlowSingleton
public class ShoppingCart {
List purchases…
}
FLOW SCOPE
MULTI ACTIVITY FLOWS
SOME EXAMPLES #DroidConBos | @D_Lemures
Slide 63
Slide 63 text
UNIT TESTING
WE TEST UNITS
#DroidConBos | @D_Lemures
Slide 64
Slide 64 text
UNIT TESTING
WE TEST UNITS
MOCK
USES
WE MOCK
DEPENDENCIES
#DroidConBos | @D_Lemures
Slide 65
Slide 65 text
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
Slide 66
Slide 66 text
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
Slide 67
Slide 67 text
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