Transcript
Page 1: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Automated testing Android

Dependency Injection and Dagger

Page 2: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Dependency InjectionDagger

Live Coding Sample 1Live Coding Sample 2

Page 3: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

The Business Goal

Page 4: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Why not automated tests on mobile?

Page 5: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Motivation for Dependency Injection

● Decouple concrete from concrete● Uniformity● Reduced Dependency Carrying● More Testable Code

Page 6: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Decouple concrete from Concreteclass MyStringUtils {

private Context context;

StringUtils(Context context) { this.context = context; }

public String helloWorld() { return context.getString(R.string.hello_ciklum); }}

Page 7: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Decouple concrete from Concreteclass MainActivity extends Activity implements View.OnClickListener {

@Inject MyStringUtils myStringUtils;

void onCreate(Bundle savedInstanceState) { … }

void onClick(View v) { MyStringUtils myStringUtils1 = new MyStringUtils(this.getApplication()); // new operator MyStringUtils myStringUtils2 = MyStringUtils.getInstance(this); // singleton pattern MyStringUtils myStringUtils3 = MyStringUtilsFactory.getInstance(this); // factory patterns String str1 myStr = MyStringUtils.helloWorld(this); // Static

String str = myStringUtils.helloWorld(); TextView msgView = (TextView) findViewById(R.id.textView); msgView.setText(str); }}}

Page 8: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Uniformityclass MainActivity extends Activity implements View.OnClickListener {

@Inject MyStringUtils myStringUtils;

void onCreate(Bundle savedInstanceState) { … }

void onClick(View v) { MyStringUtils myStringUtils1 = new MyStringUtils(this.getApplication()); // new instance MyStringUtils myStringUtils2 = MyStringUtils.getInstance(this); // Singleton pattern MyStringUtils myStringUtils3 = MyStringUtilsFactory.getInstance(this); // Factory pattern String str1 myStr = MyStringUtils.helloWorld(this); // Static String str = myStringUtils.helloWorld(); TextView msgView = (TextView) findViewById(R.id.textView); msgView.setText(str); } }}

Page 9: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Dependency Carryingclass MyActivity extends Activity {

onClick(View v) {A a = new A(this);a.doSometing();

}}

class A {Context mContext;public (Context mContext){

this.mContext = mContext;}public doSomething() {

B b = new B(mContext);String str =

b.getSomeString(R.strings.helloWorld);}

}

class B {Context mContext;public B(Context mContext) {

this.mContext = mContext;}

public String getSomeString(int resourceId) {return

mContext.getString(resourceId);}

}

Page 10: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Reduced Dependency Carrying@Module class ProdModule {

Context mContext;public ProdModule(Context mContext) {

this.mContext = mContext;}@Provide B provideB() {

return new B(context);}@Provide A provideA(B b) {

return new A(b);}}class MyActivity {

@Inject A a; onCreate(){

((MyApplication)getApplication()).inject(this);}

onClick(View v) {A a = new A(this);a.doSomething();

}}

class A {@Inject B b;public doSomething() {

String str = b.getSomeString(R.strings.helloWorld);}

}class B {

Context mContext;public B(Context mContext) {

this.mContext = mContext;}

public String getSomeString(int resourceId) {return mContext.getString(resourceId);

}}

Page 11: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

More Testable Codeclass MainActivity extends Activity implements View.OnClickListener {

@Inject MyStringUtils myStringUtils;

void onCreate(Bundle savedInstanceState) { … }

void onClick(View v) { String str = myStringUtils.helloWorld(); TextView msgView = (TextView) findViewById(R.id.textView); msgView.setText(str); } }}

Page 12: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Other advantages

● More Reusable Code● More Readable Code● Reduced Dependencies

Page 13: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Dependency InjectionDagger

Live Coding Sample 1Live Coding Sample 2

Page 14: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

DAGger

DirectAcyclicGraph

Page 15: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Coffee maker

public class CoffeMaker {

@Inject Heater heater;@Inject Pump pump;

public void brew() {heater.on(); pump.pump();

System.out.println("coffee!"); heater.off();}}

Page 16: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

class Thermosiphon implements Pump {

Heater heater;

Thermosiphon(Heater heater) { this.heater = heater;}

@Override public void pump() {if (heater.isHot()) {

System.out.println("=> => pumping => =>");}

}

Page 17: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Declare Dependencies

class Thermosiphon implements Pump {Heater heater;

@Inject Thermosiphon(Heater heater) { this.heater = heater;

}}

Page 18: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Satisfy Dependencies

@Moduleclass DripCoffeeModule {

@Provides Heater provideHeater() { return new ElectricHeater();

} @Provides Pump providePump(Thermosiphon pump) { return pump;

} }

Page 19: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Build the Graph

class CoffeeApp {public static void main(String[] args) {

ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule()); CoffeeMaker coffeeMaker = objectGraph.get(CoffeeMaker.class); coffeeMaker.brew();} }

Page 20: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Neat features

● Lazy<T>

● Module overrides

Page 21: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Lazy<T>

class GridingCoffeeMaker {@Inject Lazy<Grinder> lazyGrinder;public void brew() {

while (needsGrinding()) {// Grinder created once and cached.Grinder grinder = lazyGrinder.get()grinder.grind(); }} }

Page 22: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Module Overrides

@Module(includes = DripCoffeeModule.class, injects = CoffeeMakerTest.class, overrides = true)static class TestModule {@Provides @Singleton Heater provideHeater() {return Mockito.mock(Heater.class);}

}



Page 23: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Dependency InjectionDagger

Live Coding Sample 1Live Coding Sample 2

Page 24: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Live coding - Sample 1

● add dependencies (with Gradle)● create module● set up Dagger in Application context● inject dependencies to Activity● create Activity test which injects a mock

Page 25: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Add depedencies (Gradle)

dependencies { ……... compile 'com.squareup.dagger:dagger:1.2.1' compile 'com.squareup.dagger:dagger-compiler:1.2.1'

androidTestCompile 'org.mockito:mockito-core:1.9.5' androidTestCompile 'com.google.dexmaker:dexmaker:1.0' androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.0'}

Page 26: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Module@Module( injects = { MyStringUtils.class, MainActivity.class })class ProdModule {

Application application;

ProdModule(Application application){ this.application = application; }

@Provides @Singleton MyStringUtils provideMyStringUtils() { return new MyStringUtils(application); }

Page 27: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Define Dagger Applicationclass MyApplication extends Application {

ObjectGraph mGraph;

void onCreate() { super.onCreate(); mGraph = ObjectGraph.create(getModules().toArray()); }

void inject(Object o){ mGraph.inject(o); }

List<Object> getModules() { List<Object> result = new ArrayList<Object>(); result.add(new ProdModule(this)); return result; }}

Page 28: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Create MyStringUtils class MyStringUtils {

MyStringUtils(Context context) { this.context = context; }

public String helloWorld() { return context.getString(R.string.hello_ciklum); }}

Page 29: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Inject dependencies Activityclass MainActivity extends Activity {

@Inject MyStringUtils myStringUtils; void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // set dependencies to this activity ((MyApplication)getApplication()).inject(this);

setContentView(R.layout.activity_main); findViewById(R.id.button).setOnClickListener(this); }

Page 30: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Unit test Activity with mockclass MainActivityUnitTest extends ActivityUnitTestCase<MainActivity> {

@Inject MyStringUtils myStringUtils;

void setUp() { // create test application context, Dagger Graph with our test module. MyApplication application = new TestApplication(); application.inject(this); // inject the dependencies we need to this class setApplication(application); // use our custom test application context }

void testOnClick() { String testingStr = "olala"; when(myStringUtils.helloWorld()).thenReturn(testingStr);

this.activity = startActivity(intent, null, null);

// the test View view = activity.findViewById(R.id.button); activity.onClick(view);

// verify the mock was invoked verify(myStringUtils, times(1)).helloWorld();

// assert view got updated correctly TextView msgView = (TextView) activity.findViewById(R.id.textView); assertEquals(testingStr, msgView.getText()); }

Page 31: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Dependency InjectionDagger

Live Coding Sample 1Live Coding Sample 2

Page 32: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Sample app 2

● Threads● HTTP mocks

Page 33: Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

Tips, tricks and Frameworks

● https://github.com/tha022/dagger-testing-example● https://github.com/fizz-buzz/fb-android-dagger