Test Drive Android with Robolectric - Atlanta Android Meetup

43
Android Unit Test Framework http://pivotal.github.com/robolectric Follow us on twitter: @robolectric Tuesday, March 22, 2011

description

Test drive your Android applications with Robolectric! Links: Robolectric: http://pivotal.github.com/robolectric/ Code from the talk: https://github.com/joemoore/RobolectricSample IntelliJ CE (free!): http://www.jetbrains.com/idea/download/index.html Note: I barowed heavily from Tyler Schultz's Robolectric side deck: http://www.slideshare.net/tylerschultz/robolectric-android-unit-testing-framework

Transcript of Test Drive Android with Robolectric - Atlanta Android Meetup

Page 1: Test Drive Android with Robolectric - Atlanta Android Meetup

Android Unit Test Framework

http://pivotal.github.com/robolectricFollow us on twitter: @robolectric

Tuesday, March 22, 2011

Page 2: Test Drive Android with Robolectric - Atlanta Android Meetup

Joe Moore@joem

Pivotal Labs@pivotallabs

Tuesday, March 22, 2011

Page 3: Test Drive Android with Robolectric - Atlanta Android Meetup

Talking Points

• Testing Approaches and Alternatives

• How Robolectric works

• How to extend Robolectric

• Workshop - write tests & help getting you setup

Tuesday, March 22, 2011

Page 4: Test Drive Android with Robolectric - Atlanta Android Meetup

Disclaimer

• I’m more of a Ruby guy now.

• My Java Fu is lacking!

Tuesday, March 22, 2011

Page 5: Test Drive Android with Robolectric - Atlanta Android Meetup

Pivotal Labs

• www.pivotallabs.com

• San Francisco (Headquarters), New York, Boulder, Singapore

• Primarily Rails - we do mobile too!

• Agile, XP, Continuos Integration, Pair Programming

Tuesday, March 22, 2011

Page 6: Test Drive Android with Robolectric - Atlanta Android Meetup

Pivotal Labs

• Jasmine - Javascript BDD test framework, @jasminebdd

• Cedar - iOS/Objective-C BDD test framework, @cedarbdd

• Pivotal Tracker - www.pivotaltracker.com

You may have heard of us:

Tuesday, March 22, 2011

Page 7: Test Drive Android with Robolectric - Atlanta Android Meetup

java.lang.RuntimeException(“Stub!”)

Tuesday, March 22, 2011

Page 8: Test Drive Android with Robolectric - Atlanta Android Meetup

Google has stripped the classes in the android.jar file and

have had all their method bodies replaced with:

java.lang.RuntimeException(“Stub!”)

Tuesday, March 22, 2011

Page 9: Test Drive Android with Robolectric - Atlanta Android Meetup

Additional Android testing challenges

• Many of the classes and methods are final

• Lack of interfaces

• Non public constructors

• static methods

Tuesday, March 22, 2011

Page 10: Test Drive Android with Robolectric - Atlanta Android Meetup

How have you been testing?

Tuesday, March 22, 2011

Page 11: Test Drive Android with Robolectric - Atlanta Android Meetup

Android Testing Approaches

Tuesday, March 22, 2011

Page 12: Test Drive Android with Robolectric - Atlanta Android Meetup

Android Testing Approaches

• No Tests! EGAD!

Tuesday, March 22, 2011

Page 13: Test Drive Android with Robolectric - Atlanta Android Meetup

Android Testing Approaches

• No Tests! EGAD!

• Android InstrumentationTests/Robotium - integration style testing of Android apps

Tuesday, March 22, 2011

Page 14: Test Drive Android with Robolectric - Atlanta Android Meetup

Android Testing Approaches

• No Tests! EGAD!

• Android InstrumentationTests/Robotium - integration style testing of Android apps

• Library of tested POJO’s, referenced from a non tested Android project

Tuesday, March 22, 2011

Page 15: Test Drive Android with Robolectric - Atlanta Android Meetup

Android Testing Approaches

• No Tests! EGAD!

• Android InstrumentationTests/Robotium - integration style testing of Android apps

• Library of tested POJO’s, referenced from a non tested Android project

• Mocking framework such as Easy Mock and Mockito

Tuesday, March 22, 2011

Page 16: Test Drive Android with Robolectric - Atlanta Android Meetup

It’s Getting Better

• Robotium

• AndroidMock

• Calculon

• Robolectric

Tuesday, March 22, 2011

Page 17: Test Drive Android with Robolectric - Atlanta Android Meetup

Robolectric

Why use Robolectric?What makes it so great?

Tuesday, March 22, 2011

Page 18: Test Drive Android with Robolectric - Atlanta Android Meetup

Why Use Robolectricvs. Android Instrumentation Tests?

• Run FAST in a normal JVM, not on the emulator in the Dalvik VM (except for that damn regression...)

- Running in a Dalvik VM requires dexing, packaging and installation on an emulator or device - slow!

- Tests execute quickly in the JVM and execute slowly on the emulator

Tuesday, March 22, 2011

Page 19: Test Drive Android with Robolectric - Atlanta Android Meetup

Why Use Robolectricvs. Android Instrumentation Tests?

• Iterate quickly!

• The latest Pivotal Android project is using Robolectric boasting 1,047 tests that run in 28 seconds!

Tuesday, March 22, 2011

Page 20: Test Drive Android with Robolectric - Atlanta Android Meetup

Why Use Robolectricvs. POJO lib approach?

• We used to do this.

• The POJO lib approach leads to code proliferation, interfaces with multiple implementations - code bloat!

• Robolectric allows for vastly increased test coverage. Test ALL your code, not just non-Android code.

Tuesday, March 22, 2011

Page 21: Test Drive Android with Robolectric - Atlanta Android Meetup

• Mocking frameworks can lead to tests that are reverse implementation of the code

• Can lead to tests that are hard to read

• Can lead to tests that don’t help refactoring

Why use Robolectricvs. Mock approach?

Tuesday, March 22, 2011

Page 22: Test Drive Android with Robolectric - Atlanta Android Meetup

Why Use Robolectric?

• Iterate quickly

• Robolectric allows for a black box style of testing

• Test behavior instead of implementation

• High test coverage

Tuesday, March 22, 2011

Page 23: Test Drive Android with Robolectric - Atlanta Android Meetup

How does it work?

• Shadow Objects

• View and Resource Loading

Tuesday, March 22, 2011

Page 24: Test Drive Android with Robolectric - Atlanta Android Meetup

How does it work?

• Robolectric intercepts the loading of Android classes under test

• Rewrites the method bodies of Android classes (using javassist)

• Binds new shadow objects to new Android objects

• The modified Android objects proxy method calls to the shadow objects

Shadow objects

Tuesday, March 22, 2011

Page 25: Test Drive Android with Robolectric - Atlanta Android Meetup

How does it work?

• Shadows back the Android classes. i.e. ShadowImageView backs the ImageView class.

• AndroidObject.someMethod() => proxies to => ShadowAndroidObject.someMethod()

• State is recorded so it can be verified in tests

Shadow objects

Tuesday, March 22, 2011

Page 26: Test Drive Android with Robolectric - Atlanta Android Meetup

How does it work?

• Robolectric parses layout files and builds a view object tree made of Android view objects and, of course, their shadows.

• Some of the view xml attributes are applied to the view object

• Strings, string arrays, and color resources are parsed loaded too.

View and Resource Loading

Tuesday, March 22, 2011

Page 28: Test Drive Android with Robolectric - Atlanta Android Meetup

Robolectric IDE support

• We are IntelliJ guys

• Eclipse works, too

• Maven!

Tuesday, March 22, 2011

Page 29: Test Drive Android with Robolectric - Atlanta Android Meetup

Robolectric

Writing Tests

Tuesday, March 22, 2011

Page 30: Test Drive Android with Robolectric - Atlanta Android Meetup

Writing Tests

...@RunWith(RobolectricTestRunner.class)public class MyActivityTest {

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

Tests that reference Android need to be annotated:

Tuesday, March 22, 2011

Page 31: Test Drive Android with Robolectric - Atlanta Android Meetup

Writing Tests@Testpublic void shouldShowLogoWhenButtonIsPressed() {

Activity activity = new MyActivity();activity.onCreate(null);ImageView logo = (ImageView) activity.findViewById(R.id.logo);Button button = (Button) activity.findViewById(R.id.button);

assertThat(logo.getVisibility(), equalTo(View.GONE));

button.performClick();

assertThat(logo.getVisibility(), equalTo(View.VISIBLE));}

Tuesday, March 22, 2011

Page 32: Test Drive Android with Robolectric - Atlanta Android Meetup

Writing Tests

Dealing with cases where Android classes do not provide a way to retrieve object state

Tuesday, March 22, 2011

Page 33: Test Drive Android with Robolectric - Atlanta Android Meetup

Writing TestsAccessing the Shadow Object

<ImageViewandroid:id=”@+id/logo”android:layout_width=”wrap_content”android:layout_height=”wrap_content”android:src=”@drawable/logo” />

...

@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));

}

Tuesday, March 22, 2011

Page 34: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects

• @RealObject

• __constructor__

• @Implements

• @Implementation

• Robolectric.bindAllShadowClasses()

Tuesday, March 22, 2011

Page 35: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects@RealObject

• Robolectric is using reflection to instantiate the shadow object (default or no-args constructor)

• Robolectric will inject the Android object onto shadow object’s fields annotated with @RealObject

Tuesday, March 22, 2011

Page 36: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects@RealObject

@Implements(View.class)public class ShadowView {

@RealObject private View realView; private int id;...

Tuesday, March 22, 2011

Page 37: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects

• If no shadow class is registered for an Android class, the Android object’s super constructor will seek out a shadow class, up through the constructor super chain until one is found.

Tuesday, March 22, 2011

Page 38: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects__constructor__

• When Robolectric is finished instantiating the shadow object, it will attempt to invoke a method on the shadow named __constructor__ that has the same args as the Android object’s constructor

Tuesday, March 22, 2011

Page 39: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects__constructor__

public class Intent { public Intent(String action, Uri uri) { /* compiled code */ } ...}

public class ShadowIntent { public void __constructor__(String action, Uri uri) { ... } ...}

Tuesday, March 22, 2011

Page 40: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects@Implements

@Implements(View.class)public class ShadowView {

@RealObject private View realView; private int id;...

Tuesday, March 22, 2011

Page 41: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow Objects@Implementation

public class ShadowTextView {... @Implementation public CharSequence getText() { return text; }...

Tuesday, March 22, 2011

Page 42: Test Drive Android with Robolectric - Atlanta Android Meetup

Shadow ObjectsRobolectric.bindAllShadowClasses()

• Where shadow objects are registered into Robolectric

• This is a current listing of all the shadow objects provided by Robolectric

Tuesday, March 22, 2011

Page 43: Test Drive Android with Robolectric - Atlanta Android Meetup

Joe Moore@joem

Pivotal Labs@pivotallabs

Thanks!

Tuesday, March 22, 2011