Brain Flush

January 31, 2010

Signpost 1.2 released – now on Maven Central

Filed under: Software Development & Programming — Tags: , , , — Matthias @ 6:47 pm

I am happy to announce that I have released Signpost 1.2 earlier this week and I will briefly go over the changes the library has undergone. Most changes were under the hood, although there are also some minor API changes. I have rewritten much of the core code base, so as to make the library more flexible and easily extensible, something that was often requested by users.

Flexibility, extensibility, and general design improvements

I have flipped quite some bits in signpost-core, and now Signpost is finally more easily extensible and better configurable by developers. AbstractOAuthConsumer now lets subclasses override practically any step in the signing process, or even the entire signing altogether, by following the template method pattern. So if you need special treatment while collecting message parameters for signing, or want to use a different nonce generation algorithm, just override the respective methods in your subclass.

There is also a new class called SigningStrategy. This class defines how a signature is written to an HTTP request. The default behavior is to write OAuth parameters to the HTTP Authorization header (using AuthorizationHeaderSigningStrategy), but you can go ahead and create a custom strategy and configure your OAuthConsumer with it.

Speaking of customization. Should you be implementing your own OAuthConsumer, I have also improved the testability in that case tremendously. Simply inherit from OAuthConsumerTest and implement buildConsumer(). The test will then be executed for your particular consumer configuration.

There are also two API changes by which everyone will be affected. First, I have removed the SignatureMethod enum, since it was not very useful. Instead, every OAuthConsumer uses the HMac-SHA1 signature method by default, since that’s what everyone seems to be using anyway. So constructing a consumer is now simplified to:

OAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY,
                                     CONSUMER_SECRET);

If you really want to use a different signer, you can do this:

consumer.setMessageSigner(new PlaintextMessageSigner());

The second API change affects OAuthProvider. In earlier versions, consumer and provider were closely coupled. That led to problems in service oriented environments, where each object should be self-contained when exposed as a service. Thus, the retrieve*Token() methods of OAuthProvider now accept a consumer as the first argument:

String authUrl = provider.retrieveRequestToken(consumer, "http://example.com/callback");
// ...
provider.retrieveAccessToken(consumer, pin);

Android support

Signpost worked well on Android before, however, a bug in Android’s Java implementation (that would be Apache Harmony) prevented it to work correctly with certain service providers (HttpURLConnection is sending lower-case HTTP header names on Android, which breaks some server side OAuth implementations because they don’t recognize the Authorization header; see issue 20). I have therefore implemented a CommonsHttpOAuthProvider, which uses Apache HttpClient to receive tokens instead of HttpURLConnection.

New features

Signpost can now sign URLs. Just pass the consumer a URL string, and it will treat it as an HTTP GET message on the resource identified by the URL and append all necessary OAuth parameters to that URL. This can be very useful if you want to produce clickable links to protected resources:

String signedUrl = consumer.sign("http://example.com");

Another minor but useful feature is Signpost’s new debug mode. If you run your app with the -Ddebug flag, Signpost will now print the signature base string and generated signature for each request it signs to stdout. This is very useful when trying to figure out what went wrong should a server answer with 401 and you believe you did everything right:

[SIGNPOST] SBS: GET&http%3A%2F%2Ftwitter.com%2Foauth%2Frequ...
[SIGNPOST] signature: BVzjTYNjeJJwI4olm5ISHtvZ7Rc=

Bug fixes

Of course this release has seen some bug fixes, too. One of the most important ones is that Signpost does not send a blank oauth_token parameter anymore when retrieving a request token. Although permitted by the standard, and although many providers like Twitter or Google accept this, certain other providers failed when doing so (e.g. Netflix). If you still want to get the old pre-1.2 behavior, call setSendEmptyTokens(true) on your consumer.

Another important fix/enhancement corrects the way Signpost used to write to the Authorization header. Previously, it would just overwrite it with the OAuth parameters it generated, which caused problems when client needed to provide a realm paramter in the Auth header alongside the OAuth params. Signpost now remembers what had been in the Authorization header prior to message signing, appends its own params to it, and writes all of them back to the header. This should get rid of 401s where service providers expect a security realm to be set.

Finally, a problem was corrected where x-www-form-encoded body params were ignored if the content type contained parameters like encodings.

Project reports / API docs

I have also finally uploaded the API docs and other project reports generated by the Maven site plugin to GitHub pages, so you can browse the API docs without linking the JavaDoc JAR. You can find all project reports and other generated information here: http://kaeppler.github.com/signpost/project-reports.html

Maven Central

Last but certainly not least: Signpost 1.2 is now available via Maven Central, which means you do not have to add the signpost-releases repository to your POM anymore. It is available straight away in any Maven powered Java project. Om nom!

Advertisements

January 10, 2010

Introducing Calculon – a Java DSL for Android Activity testing

Filed under: Mobile Devices, Software Development & Programming — Tags: , , — Matthias @ 1:05 pm

I have been working on a new test library for Google Android lately, a project which I’m very excited about, and which I feel has loads of potential: Calculon, an Android testing library which lets you write functional tests for your Activities using a very concise and natural syntax.

I am a big fan of test-driven development, and I think there’s no arguing about the massive benefits you get from backing your software up with an automated test suite. The support for writing tests in Android, however, is lacking at best. Even though there has been a test runner since the 1.0 days, writing unit or functional tests in Android is far from being a pleasant experience. For me, the two most important things about tests are that they must be easy to write, and more importantly, easy to read.

That being said, here is a classic example of why Android testing sucks. Our test story is:

Assert that a click on ‘button’ launches a new BarActivity.

Without Calculon, what you do is this:

public void whatInHeavensNameDoesThisEvenTest() {
    final Button b = (Button) getActivity().findViewById(R.id.button);
    ActivityMonitor monitor = getInstrumentation().addMonitor(
        BarActivity.class.getCanonicalName(), null, false);
    getInstrumentation().runOnMainSync(new Runnable() {
        public void run() {
            b.performClick();
        }
    });
    getInstrumentation().waitForIdleSync();
    assertTrue(getInstrumentation().checkMonitorHit(monitor, 1));
}

So, I first have to retrieve a reference to the button that’s supposed to launch a new BarActivity. Then I have to create what Android calls an ActivityMonitor, that’s simply an object which just sits there and waits until an Activity is being launched that matches the description it was configured with. Then I have to perform the actual button click, but sorry, not allowed on the instrumentation thread, so I have to runOnMainSync(). We’re not yet there though: we have to sit and wait, because launching a new activity could take a while, so we also have to waitForIdleSync(). The assertion that follows also couldn’t be less expressive.

When I first wrote this test, my single thought was: Where the heck is my story? It’s buried under a heap of ugly boilerplate code.

I don’t care that the test runner cannot perform a view event on the instrumentation thread. I don’t care that it needs to sit and wait for the Activity to launch. I don’t care about ActivityMonitor and its crummy API. I just want to look at the test, and understand what it’s doing.

Let’s reiterate our test story:

Assert that a click on ‘button’ launches a new BarActivity.

With Calculon, this test is written as:

assertThat(R.id.button).click().starts(BarActivity.class);

Oh the beauty. Our test story was a single sentence, so is the test. That’s how it should be, really. There’s nothing worse than seeing someone else’s tests failing on the build server and having to dissect them line by line to figure out what it’s actually testing.

So let’s move on and look at some nifty Calculon tests. And remember: “Calculon never does two takes!”

Testing with Calculon

Calculon is in a very early stage of development, and currently it only supports writing functional Activity tests, using Android’s ActivityInstrumentationTestCase2. Here is what a Calculon test looks like:

public class FooTest extends FunctionalTest<FooActivity> {
    public FooTest() {
        super("com.example", FooActivity.class);
    }

    public void testStuff() {
        ...
    }
}

Nothing special here, really. You just inherit from a Calculon FunctionalTest.

Assertions

At the heart of a Calculon test are assertions. To create a new assertion, you use the assertThat() method:

public void testStuff() {
    // testing against activities
    assertThat()...
    assertThat(getActivity())...
    assertThat(someOtherActivity)...

    // testing against views
    assertThat(R.id.some_button)...
    assertThat(someButton)...

    // of course all Junit assertions work as well
    assertEquals(...)
    assertNotNull(...)
    ...
}

For now, there are two basic kinds of assertions: ActivityAssertion and ViewAssertion. There is also a third kind, ActionAssertion, but it takes a somewhat special role and is discussed at the end.

Activity Assertions

An ActivityAssertion is an assertion on the state of an Activity and is used like this:

public void testStuff() {
    // testing for an orientation
    assertThat().inPortraitMode();

    // testing for views
    assertThat().viewExists(R.id.some_button);

    // testing for input actions
    assertThat().keyDown(KeyEvent.KEYCODE_BACK)...

    // testing for custom predicates
    assertThat().satisfies(new Predicate<Activity>() {
        public boolean check(Activity target) {
            return target.isTaskRoot();
        }
    });
}

View Assertions

A ViewAssertion is basically the same, just that it tests the state and behavior of a view:

public void testStuff() {
    // testing for view state
    assertThat(R.id.button).isVisible();
    assertThat(R.id.button).isGone();

    // testing for input actions
    assertThat(R.id.button).keyDown(KeyEvent.KEYCODE_BACK)...
    assertThat(R.id.button).click()...
    assertThat(R.id.button).longClick()...

    // testing for custom predicates
    assertThat(R.id.button).satisfies(new Predicate<View>() {
        public boolean check(View target) {
            return target.getVisibility() == View.VISIBLE;
        }
    });
}

Action Assertions

This is a special kind of assertion. You always use it when you want to assert that some action (e.g. a click or a key press) does something. You can use it on both activity and view assertions, and it also allows you to delegate an assertion to another object:

public void testStuff() {
    // testing for actions that launch a new activity
    assertThat(R.id.b1).click().starts(BarActivity.class);

    // testing for actions that finish an activity
    assertThat(R.id.b2).keyDown(KeyEvent.KEYCODE_Q).finishesActivity();

    // testing for actions that change something
    assertThat(R.id.b3).click().implies(R.id.b2).isGone();
    assertThat(R.id.b4).click().implies(getActivity()).inLandscapeMode();
    assertThat(R.id.b5).click().implies(new Predicate<Model>() {
        public boolean check(Model target) {
            return target.someAttribute() == 5;
        }
    });
}

There’s more to come!

This is only a very early version. I intend to expand this library significantly over time. Of course it’s open source, so contributions are always welcome. You can fork the project on GitHub, add your changes, and send me a pull request.

Blog at WordPress.com.