droidconuk: tdd android with robolectric

53
Android Unit Test Framework http://robolectric.org @robolectric Saturday, October 8, 11

Upload: joseph-moore

Post on 09-May-2015

5.653 views

Category:

Technology


7 download

DESCRIPTION

I gave this presentation at DroidconUK on October 7, 2011 to a room full of amazing Android developers. Time was short, and thus this presentation was shorter than I usually give, with no live TDD coding.

TRANSCRIPT

Page 1: DroidconUK: TDD Android with Robolectric

Android Unit Test Frameworkhttp://robolectric.org

@robolectric

Saturday, October 8, 11

Page 2: DroidconUK: TDD Android with Robolectric

Joe Moore@joem

Pivotal Labs@pivotallabs

Saturday, October 8, 11

Page 3: DroidconUK: TDD Android with Robolectric

Disclaimer

I’m mostly a Ruby guy now

Saturday, October 8, 11

Page 4: DroidconUK: TDD Android with Robolectric

Agenda

• Testing Approaches and Alternatives

• How Robolectric works

• How to extend Robolectric

Saturday, October 8, 11

Page 5: DroidconUK: TDD Android with Robolectric

Software consulting company, primarily Ruby on Rails, Android, and iOS

XP, TDD, Pair Programming

SF, NYC, Boulder CO, SingaporeSaturday, October 8, 11

Page 6: DroidconUK: TDD Android with Robolectric

BDD for iOSCedar

JsUnit

We Test StuffGreat Expectations

Jasmine-style assertions for JUnit

Saturday, October 8, 11

Page 7: DroidconUK: TDD Android with Robolectric

java.lang.RuntimeException(“Stub!”)

Saturday, October 8, 11

Page 8: DroidconUK: TDD Android with Robolectric

Google replaced the method bodies in android.jar with:

java.lang.RuntimeException(“Stub!”)

Saturday, October 8, 11

Page 9: DroidconUK: TDD Android with Robolectric

java.lang.RuntimeException: Stub!! at android.content.Context.<init>(Context.java:4)! at android.content.ContextWrapper.<init>(ContextWrapper.java:5)! at android.view.ContextThemeWrapper.<init>(ContextThemeWrapper.java:5)! at android.app.Activity.<init>(Activity.java:6)! at com.pivotallabs.NamesActivity.<init>(NamesActivity.java:9)

Every JUnit Test Fails

Saturday, October 8, 11

Page 10: DroidconUK: TDD Android with Robolectric

Additional Android Testing Challenges

• Many of the classes, methods are final

• Lack of interfaces

• Non public constructors

• Static methods

Saturday, October 8, 11

Page 11: DroidconUK: TDD Android with Robolectric

It’s Getting Better

CalculonA testing DSL for Google Android

Saturday, October 8, 11

Page 12: DroidconUK: TDD Android with Robolectric

How have you been testing?

Saturday, October 8, 11

Page 13: DroidconUK: TDD Android with Robolectric

Android Testing Approaches

Saturday, October 8, 11

Page 14: DroidconUK: TDD Android with Robolectric

Android Testing Approaches

• Integration-style

Saturday, October 8, 11

Page 15: DroidconUK: TDD Android with Robolectric

Android Testing Approaches

• Integration-style

•Library of tested POJO’s

Saturday, October 8, 11

Page 16: DroidconUK: TDD Android with Robolectric

Android Testing Approaches

• Integration-style

•Library of tested POJO’s

•Mocking framework

Saturday, October 8, 11

Page 17: DroidconUK: TDD Android with Robolectric

Android Testing Approaches

• Integration-style

•Library of tested POJO’s

•Mocking framework

•F@*K IT!

Saturday, October 8, 11

Page 18: DroidconUK: TDD Android with Robolectric

Why use Robolectric?

Saturday, October 8, 11

Page 19: DroidconUK: TDD Android with Robolectric

Features• FAST!

• Interact with Android code.

• Parsing, loading of layouts, strings.xml, colors.xml, etc.

• State tracking of many Android objects

• Amazing HTTP/API testing

• Wonderful static helpers methods allowing instantiation of privates and other... stuff :)

• Supports Android frameworks like RoboGuice, android-mock, and other Android frameworks.

Saturday, October 8, 11

Page 20: DroidconUK: TDD Android with Robolectric

Instrumentation Tests require dexing, packaging and installation on an emulator or device to run in the Dalvik VM.

vs. Android Instrumentation Tests?Instrumentation Tests are

Saturday, October 8, 11

Page 21: DroidconUK: TDD Android with Robolectric

Robolectric runs in the regular JVM:

No dexing, packaging, deploying, etc.

vs. Android Instrumentation Tests?

FAST

Saturday, October 8, 11

Page 22: DroidconUK: TDD Android with Robolectric

Robolectric lets you:

• Use Ye Olde JUnit

• Iterate quickly

• Test behavior instead of implementation

• Have high test coverage

• Supports the way Android developers are taught to develop (for better or for worse.)

Saturday, October 8, 11

Page 23: DroidconUK: TDD Android with Robolectric

• http://robolectric.org

• http://github.com/pivotal/robolectric/

• http://github.com/pivotal/RobolectricSample

How can I get started?

Saturday, October 8, 11

Page 24: DroidconUK: TDD Android with Robolectric

Writing Tests

Saturday, October 8, 11

Page 25: DroidconUK: TDD Android with Robolectric

Writing TestsTests that reference Android need to be annotated:

@RunWith(RobolectricTestRunner.class)public class MyActivityTest {

@Test! public void shouldDoWizbangFooBar() {...

Saturday, October 8, 11

Page 26: DroidconUK: TDD Android with Robolectric

Writing TestsSometimes you do not even see Robolectric

(Where is Robolectric? Nice and hidden!)

@RunWith(RobolectricTestRunner.class)public class MyActivityTest { private View homeButton; private Activity activity ...

@Test! public void shouldDoWizbangFooBar() { homeButton = activity.findViewById(R.id.home);

Saturday, October 8, 11

Page 27: DroidconUK: TDD Android with Robolectric

...

@Testpublic void logoImageViewShouldUseTheLogoDrawable() {

ImageView logo = (ImageView) activity.findViewById(R.id.logo);// imageView only provides logo.getDrawable();ShadowImageView logoShadow = Robolectric.shadowOf(logo);assertThat(logoShadow.resourceId, equalTo(R.drawable.logo));

}

Writing TestsTalking to Shadows

Saturday, October 8, 11

Page 28: DroidconUK: TDD Android with Robolectric

HTTP Testing

java.lang.RuntimeException: Unexpected call to execute, no pending responses are available. See Robolectric.addPendingResponse().

Request was: GET http://example.com/foo/bar.json?page=28...

Unexpected HTTP Calls Throw Exceptions

Saturday, October 8, 11

Page 29: DroidconUK: TDD Android with Robolectric

HTTP TestingHTTP Handling Methods

public class Robolectric

Saturday, October 8, 11

Page 30: DroidconUK: TDD Android with Robolectric

HTTP Testing

new FakeHttpLayer.RequestMatcherBuilder() .host("http://example.com") .path("foo/bar.json") .method("GET") .param("page","28") .header("header1", "someValue");

Robolectric.addHttpResponseRule(requestMatcher, response)

Saturday, October 8, 11

Page 31: DroidconUK: TDD Android with Robolectric

HTTP TestingRobolectric.addHttpResponseRule(requestMatcher, response)

new TestHttpResponse(200, "{ 'jsonObj': {'key': 'value'}");

new TestHttpResponse(200, Fixtures.load(“users.json”);

Verifying that your app can parse API data is a good idea!

Saturday, October 8, 11

Page 32: DroidconUK: TDD Android with Robolectric

HighlightsGetting the Latest...

ShadowAlertDialog.getLatestDialog()ShadowAlertDialog.getLatestAlertDialog()ShadowToast.getLatestToast()

myShadowNotification.getLatestEventInfo()

Robolectric.getShadowApplication().getNextStartedActivity()~ or ~ .getNextStartedService()

Saturday, October 8, 11

Page 33: DroidconUK: TDD Android with Robolectric

HighlightsLoopers, Schedulers

ShadowLooper.getMainLooper()

shadowMainLooper.getScheduler()

Robolectric.pauseMainLooper()

Robolectric.unPauseMainLooper()

Saturday, October 8, 11

Page 34: DroidconUK: TDD Android with Robolectric

How does it work?

Saturday, October 8, 11

Page 35: DroidconUK: TDD Android with Robolectric

Shadow Objects

“Shadow” implementations of Android classes

Saturday, October 8, 11

Page 36: DroidconUK: TDD Android with Robolectric

Shadow Objects

Saturday, October 8, 11

Page 37: DroidconUK: TDD Android with Robolectric

Button(Android)

ShadowButtonmyButton.getText() getText()

text=“Okay”return “Okay”return “Okay”

Shadow Objects

Saturday, October 8, 11

Page 38: DroidconUK: TDD Android with Robolectric

Button(Android)

ShadowButton

myButton.getSomething() getSomething()

Does not implement getSomething()

return null;

Shadow Objects

Saturday, October 8, 11

Page 39: DroidconUK: TDD Android with Robolectric

How does it work?1. Robolectric intercepts the loading of Android

classes under test

Saturday, October 8, 11

Page 40: DroidconUK: TDD Android with Robolectric

How does it work?1. Robolectric intercepts the loading of Android

classes under test

2. Rewrites the method bodies of Android classes (using javassist)

Shadow Objects

Saturday, October 8, 11

Page 41: DroidconUK: TDD Android with Robolectric

How does it work?1. Robolectric intercepts the loading of Android

classes under test

2. Rewrites the method bodies of Android classes (using javassist)

3. Binds “shadow objects” to new Android objects

Shadow Objects

Saturday, October 8, 11

Page 42: DroidconUK: TDD Android with Robolectric

How does it work?1. Robolectric intercepts the loading of Android

classes under test

2. Rewrites the method bodies of Android classes (using javassist)

3. Binds “shadow objects” to new Android objects

4. The modified Android objects then proxy method calls to the shadow objects

Shadow Objects

Saturday, October 8, 11

Page 43: DroidconUK: TDD Android with Robolectric

What About Using “Real” Android Jars?

• Brach on github: “reviscerated”

• Blog article: robolectric.blogspot.com

• It’s hard: hand-building jars, native code, indeterminate expectations.

Saturday, October 8, 11

Page 44: DroidconUK: TDD Android with Robolectric

Where do the Shadow Implementations come From?

Us, and you!

This project is open source and maintained by the Robolectric community.

Please contribute at http://github.com/pivotal/robolectric

Saturday, October 8, 11

Page 45: DroidconUK: TDD Android with Robolectric

Extending Robolectric

Help Robolectric cover moreof Android

Saturday, October 8, 11

Page 46: DroidconUK: TDD Android with Robolectric

Shadow Objects

Shadow inheritance works.

Some shadows are not implemented because most of their functionality is inherited.

Saturday, October 8, 11

Page 47: DroidconUK: TDD Android with Robolectric

Writing Shadow Objects

• @Implements

• @Implementation

• Robolectric.getDefaultShadowClasses()

• __constructor__

• @RealObject

Saturday, October 8, 11

Page 48: DroidconUK: TDD Android with Robolectric

Shadow Objects@Implements

@Implements(View.class)public class ShadowView { ...}

Saturday, October 8, 11

Page 49: DroidconUK: TDD Android with Robolectric

Shadow Objects@Implementation

... @Implementation public int getId() { return this.id; } ...

Saturday, October 8, 11

Page 50: DroidconUK: TDD Android with Robolectric

Shadow ObjectsRobolectric.getDefaultShadowClasses()

return Arrays.asList( ShadowAbsListView.class, ShadowAbsoluteLayout.class, ShadowAbsSeekBar.class, ShadowAbsSpinner.class, ShadowAbstractCursor.class, ShadowActivity.class, ShadowActivityInfo.class, ...

Saturday, October 8, 11

Page 51: DroidconUK: TDD Android with Robolectric

Shadow Objects__constructor__

public class View { public View(Context context) { /* compiled code */ } ...

public class ShadowView { public void __constructor__(Context context) { ... } ...

Saturday, October 8, 11

Page 52: DroidconUK: TDD Android with Robolectric

Shadow Objects@RealObject

@Implements(View.class)public class ShadowView { @RealObject private View realView; ...

Saturday, October 8, 11

Page 53: DroidconUK: TDD Android with Robolectric

Joe Moore@joem

Pivotal Labs@pivotallabs

Thanks!

Saturday, October 8, 11