Test Drive Android with Robolectric - Atlanta Android Meetup

Post on 29-Nov-2014

1.713 views 3 download

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

Android Unit Test Framework

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

Tuesday, March 22, 2011

Joe Moore@joem

Pivotal Labs@pivotallabs

Tuesday, March 22, 2011

Talking Points

• Testing Approaches and Alternatives

• How Robolectric works

• How to extend Robolectric

• Workshop - write tests & help getting you setup

Tuesday, March 22, 2011

Disclaimer

• I’m more of a Ruby guy now.

• My Java Fu is lacking!

Tuesday, March 22, 2011

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

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

java.lang.RuntimeException(“Stub!”)

Tuesday, March 22, 2011

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

Additional Android testing challenges

• Many of the classes and methods are final

• Lack of interfaces

• Non public constructors

• static methods

Tuesday, March 22, 2011

How have you been testing?

Tuesday, March 22, 2011

Android Testing Approaches

Tuesday, March 22, 2011

Android Testing Approaches

• No Tests! EGAD!

Tuesday, March 22, 2011

Android Testing Approaches

• No Tests! EGAD!

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

Tuesday, March 22, 2011

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

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

It’s Getting Better

• Robotium

• AndroidMock

• Calculon

• Robolectric

Tuesday, March 22, 2011

Robolectric

Why use Robolectric?What makes it so great?

Tuesday, March 22, 2011

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

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

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

• 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

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

How does it work?

• Shadow Objects

• View and Resource Loading

Tuesday, March 22, 2011

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

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

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

Robolectric IDE support

• We are IntelliJ guys

• Eclipse works, too

• Maven!

Tuesday, March 22, 2011

Robolectric

Writing Tests

Tuesday, March 22, 2011

Writing Tests

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

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

Tests that reference Android need to be annotated:

Tuesday, March 22, 2011

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

Writing Tests

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

Tuesday, March 22, 2011

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

Shadow Objects

• @RealObject

• __constructor__

• @Implements

• @Implementation

• Robolectric.bindAllShadowClasses()

Tuesday, March 22, 2011

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

Shadow Objects@RealObject

@Implements(View.class)public class ShadowView {

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

Tuesday, March 22, 2011

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

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

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

Shadow Objects@Implements

@Implements(View.class)public class ShadowView {

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

Tuesday, March 22, 2011

Shadow Objects@Implementation

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

Tuesday, March 22, 2011

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

Joe Moore@joem

Pivotal Labs@pivotallabs

Thanks!

Tuesday, March 22, 2011