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

33
Automated testing Android Dependency Injection and Dagger

description

We are happy to invite you to the Speakers’ Corner today, on Thursday May 29, from 18.30 till 19.30 at SkyPoint to meet Thomas Vervik, Head of Development Bipper Communications who will talk on “How to save money on QA - Dependency Injection and automated testing on Android” Thomas is Head of Development for Bipper Communications, and has been managing the company's team in Kiev since February 2012. Originally a seasoned Java server backend/frontend developer, he has the last two years started mobile development, first with HTML 5 and later Android. Mobile development has since its birth around 2008 gone from simple apps to more complex enterprise similar software. The increase in size and complexity yields the need for structuring the code differently in order to handle the new complexity. The tools used to handle this complexity has been applied to server side development for years, but mobile development has been lagging behind. But not anymore. New frameworks built on proven paradigms are emerging, and in this Speakers Corner we will introduce Dependency Injection for Android, the motivation for its use, and one of the implementations - Dagger. Dependency Injection has several advantages, but in this presentation we will focus on how it enables to write proper automated tests.

Transcript of Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014

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