Sunday, February 8, 2009

Unit Test Your Custom Parcelable

Your Android application needs to pass some custom data between processes in an Intent. You will be calling Intent.putExtra(String, Parcelable). It would be easier to test if the Parcel class where not final with only private constructors. That keeps us from testing the methods individually. We are forced to perform only round trip testing -- write our Parcelable to a Parcel, then call the creator with the same Parcel instance.

There is one important step right in the middle of the round trip. The Parcel needs to be reset to be ready for read. Think of this just like working with java.nio.ByteBuffer. With ByteBuffer, when you are done writing, you call flip. With Parcel, when you want to read -- call setDataPosition(0). Here is a sample test.

I'll decoreate a Person object as a ParcelablePerson that implements the Parcelable interface and has the requisite public static final CREATOR. Following TDD I've only created enough of the implementation code below to make the test compile.
    public void testPersonTakesRoundTripThroughParcel() throws Exception {
Person testPerson = new Person();
ParcelablePerson testObject = new ParcelablePerson(testPerson);
Parcel parcel = Parcel.obtain();
testObject.writeToParcel(parcel, 0);
//done writing, now reset parcel for reading
parcel.setDataPosition(0);
//finish round trip
ParcelablePerson createFromParcel = ParcelablePerson.CREATOR.createFromParcel(parcel);

assertEquals(testPerson, createFromParcel.getPerson());
}
public class Person {
...
}
public class ParcelablePerson implements Parcelable {
private Person person;
public ParcelablePerson(Person person) {
this.person = person;
}

public Person getPerson() {
return person;
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
//call dest.write... methods
}

public static final Creator CREATOR = new Creator() {
@Override
public ParcelablePerson createFromParcel(Parcel source) {
return null;
}

@Override
public ParcelablePerson[] newArray(int size) {
return null;
}};
}

Thursday, January 29, 2009

Android View XML

I am thoroughly enjoying using XML to define the view characteristics of my Android application. I've written GUI code in Visual Basic, Swing, RCP, and a dab of GTK+. None of them are as easy and elegant as using XML. I applaud the Android team in using this approach.

Don't forget to apply some style to your view. Who would want to look at some boring, white standard font. Checkout the Android documentation on styles and themes.

But don't stop at using only the view components that Android supplies. Good object oriented design encompasses creating custom view components for your custom data objects. If you have a domain object called Person that needs to be displayed then you should have a custom composite view that knows exactly how a Person is displayed -- err, the attributes of the person are displayed. I learned over here that using custom view controls in the XML is as easy as defining an xml element with the name of your custom view.

I put together the below example as a guide. It is not meant to be an exemplar. Note that i have a LinearLayout that defines padding. That likely belongs in a style instead of being defined solely for THAT LinearLayout. With that warning in place, here's an exmple of a my.app.PersonComposite which is a custom class that extends LinearLayout. To use this custom view component in your XML then you would include something like the following:
<my.app.PersonComposite
android:id="@+id/person"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
So, that element will put a PersonComposite in your activity. But what does the PersonComposite look like? Well, that should be defined in xml as well. In the constructors of PersonComposite, include the following:
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View inflatedView = inflater.inflate(R.layout.person_composite, this);

In order for this to work, you'll need to have a ./res/layout/person_composite.xml. Perhaps something like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:padding="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<TextView
style="@style/BigAndBlue"
android:id="@+id/personLastName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_given_default"
/>

<TextView
android:id="@+id/personFirstName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/not_given_default"
/>
</LinearLayout>
Note that the style attribute conspicuously lacks the android namespace. Hmm? Oh well, just watch out for that. We have to define that Style over in ./res/values/styles.xml:
<resources>
<style name="BigAndBlue">
<item name="android:textSize">12pt</item>
<item name="android:textColor">#008</item>
</style>
</resources>

Tuesday, January 13, 2009

Unit Testing Android Applications

Unit testing my Android application has put strain on my patience. All of my unit tests are not just one quick key stroke. I already posted regarding mock testing an Android project. Integration tests are another issue. These tests have to run inside the container -- the emulator or phone. I got started writing integration tests by reading this document. You'll find more Android topics discussed at his blog.

I followed Diego's steps to setup the test-specific eclipse project. Then I created an external tool configuration to launch the tests:
The "Functional Tests" run configuration changes the arguments to:
shell am instrument -w -e func true com.wilson.android.libary.test/android.test.InstrumentationTestRunner
Now each time I'm ready to run the tests I need to deploy the main project, the test project -- if they both changed. Now I can use one of the run configurations above to use "adb" to launch the tests.

Be sure to read the official documentation on running instrumentation tests, starting here.

Tuesday, December 23, 2008

Android: The New Container

In the early days of Java Servlet development back in 1996 we had very little tool support. As the tools evolved the teams I was a part of started incorporating multiple build paths. The code was built in the IDE, but it was also built on the desktop using ant. This was good because the developers were building the same way as Cruisecontrol.

I think Android is going to be no different. I see a strong correlation between writing and testing servlets to run in the J2EE container and writing GUI and services for Android. For me this is marking a return to a platform with week tool support. The weakness I'm first percieving is unit testing. I have not yet found how to press one button to run all the tests. That is, one button press will launch the emulator (if needed), hot deploy the code to the container/emulator, and execute the in-container tests. Then there is the out-of-container tests that have no dependency upon the container.

Monday, December 22, 2008

Google Docs Publish to Blog Feature

My Android & Mockito post came from Google docs - publish to blog - feature. I had much higher expectations that the formatting wouldn't get mangled. My code segments lost their indent! I must have missed some requirement. Just my style to initially think I've done something wrong or the problem would have been fixed by now. I'll think that way until I prove myself wrong, and so doing prove myself right -- that I used the feature right.

Android & Mockito

During the day I'm a mild mannered (debatable) Java developer currently paid to work on a multi-threaded server with it's own RCP client. This is important because I've started to pick up some habits from that team. One habit in particular is the importance of using a mocking framework. The day job uses jMock. I find the expectations blocks clumsy things that run against the flow of a non-mocking JUnit test. As a result I've started using Mockito on my own projects. I try to avoid theological debates out this framework or that. It's more important that I like the public interface. It doesn't decrease the value of a mocking (or stubing) framework.

My wife recently encouraged me to buy an HTC G1 phone. She's great, isn't she! Two days later, I'm using the development kit, working on my first application. After poking around at the API, and getting the lay of the land it was time to get serious and understand how to interact with the platform. To that end I need to write some unit tests.

So I want to write a unit test for an implementation of android.view.View.OnClickListener. The interface has only one method, no surprise here, it's an onClick method: "public void onClick(View view)". Here's my implementation:
class MyOnclickListener implements OnClickListener {
public void onClick(View view) {
Intent intent = new Intent("a.unique.string");
intent.addCategory(Intent.CATEGORY_DEFAULT);
activity.startActivityForResult(intent, 1);
}
}
What the "activity.startActivityForResult" method does is not germane to this story. Just know that I need a test that makes sure that method is called with the right Intent. How do I know it's the right Intent object? So here's my first pass at a test:
@Test
public void onClick_WithRealIntent() throws Exception {
Activity mockActivity = Mockito.mock(Activity.class);
View mockView = Mockito.mock(View.class);

MyOnclickListener testObject = new MyOnclickListener(mockActivity);
testObject.onClick(mockView);

Intent expectedIntent = new Intent("com.google.zxing.client.android.SCAN");
Mockito.verify(mockActivity).startActivityForResult(expectedIntent, 1);
}
I'm betting here that the Intent object has an equals method based on that constructor argument. But running this test fails horribly. Now, I should note that in RCP fashion I've created a separate project to hold my unit tests. So the test project (in Eclipse) depends on the "real" project. Both projects have the android buildSpec and nature.
#
# An unexpected error has been detected by Java Runtime Environment:
#
# Internal Error (434C41535326494C453041525345520E4350500B65), pid=14086, tid=3084753808
#
# Java VM: Java HotSpot(TM) Server VM (1.6.0_03-b05 mixed mode)
# An error report file with more information is saved as hs_err_pid14086.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#
So I poke around after the crash file reveals little to no details of what happened. And low, and behold, there is evidence that android junit run configurations fail by default. So we have to edit the run configuration as suggested and try again. I'll point out that I'm not exactly as they suggest. I'm using Junit4, my bootstrap classpath is only the JRE library, and I added android.jar to the UserEntries under the default classpath.
java.lang.RuntimeException: Stub!
at android.content.Intent.<init>(Intent.java:27)
...
Argh! What's that mean! We could take a look at the android source code to see what happens on Intent.java:27. Our implementation class will still have to call "Intent intent = new Intent("a.unique.string");". Now comes in another habit picked up from the day job. I'm still not sure if this habit is a good one. When dealing with the JMock tests for the projects server there is a series of "provider" classes. These are really simple factory pattern classes that don't have any logic, they just new up an object, they they provide instead of factor(y). Here's what I mean:
public class MyOnclickListener implements OnClickListener {
private final Activity activity;
private final IntentProvider intentProvider;

public MyOnclickListener(Activity activity) {
this(activity, new IntentProvider());
}

MyOnclickListener(Activity activity, IntentProvider intentProvider) {
this.activity = activity;
this.intentProvider = intentProvider;
}

public void onClick(View view) {
Intent intent = intentProvider.provideIntent();

activity.startActivityForResult(intent, 1);
}

static class IntentProvider {
public Intent provideIntent() {
Intent intent = new Intent("a.unique.string");
intent.addCategory(Intent.CATEGORY_DEFAULT);
return intent;
}
}
That package visible constructor is for our test. Now our test can use a mock Intent because the test is going to provide a mock IntentProvider:
@Test
public void onClick_startsActivity_WithTheRightIntent() throws Exception {
Activity mockActivity = Mockito.mock(Activity.class);
Intent mockIntent = Mockito.mock(Intent.class);

Mockito.when(mockIntentProvider.provideIntent()).thenReturn(mockIntent);

MyOnclickListener testObject = new MyOnclickListener(mockActivity, mockIntentProvider);
View mockView = Mockito.mock(View.class);
testObject.onClick(mockView);

Mockito.verify(mockActivity).startActivityForResult(mockIntent, 1);
}
and now we have a green bar. But at what cost? This is an important question. We just created a constructor and an inner class JUST FOR THE TEST. Those two elements doubled the amount of code in MyOnClickListener! This is not the end. I'll contiue to analyze this issue.

Wednesday, November 12, 2008

ThinkPad Dual Screen

I needed to use the VGA output on my Thinkpad to give a demonstration on Java Connector Architecture. A very short search later led me to this forum posting http://ubuntuforums.org/showthread.php?t=346555 that has a link to this http://ozlabs.org/~jk/docs/mergefb/.

Now I can use xrandr to turn on and off rudimentary screen mirror.
xrandr --output VGA --auto
xrandr --output VGA --off

I'll update here when I successfully get the desktop spread across both screens. As a word of warning, I just turned of xrandr VGA output (--off) and all running applications were locked. I could swap focus between them but the applications themselves did not respond. Beware.