03 March 2016

Extraordinary, Driven, Passionate, Imaginative, Remarkable, Phenomenal, Seasoned Developer Wanted. Apply within.

Recently spotted on the Internet:

Open Positions

  • Extraordinary DevOps Leader
  • Driven JavaScript / Node.js Developer
  • Remarkable PHP Engineer
  • Imaginative iOS Developer
  • Passionate UX/UI Designer
  • Seasoned Python Developer
(Really. I didn't make these up.)

I wondered if I'd qualify for any of those, if I have the necessary qualities, even supposing I possessed the requisite hard skills...

Extraordinary?

No, I don't think I am extraordinary. Most humans are not extraordinary. Most humans are pretty ordinary. That said, I've certainly done some unusual things. The most unusual was probably dropping out of my corporate programming and design job, with all its advantages of good income, city lifestyle and boringboringboringboring to live out in the sticks on a rural smallholding, trying to be self-sufficientish, growing my own food, supplying my own water, brewing my own beer, and learning. Always learning...

Driven?

Yes, I confess, I have been driven.

Back in late 1999 into 2000 I did a gig that involved me living and working in Switzerland for about six months, on and off. Not in Switzerland as much as in one particular Canton that has (I was told) specific IP treaties with various other parts of the world that were (I was told) essential to the success of The Venture.

I was accommodated in particularly upmarket lodgings -- an apartment in the same building that housed the offices and personal home-away-from-home of one of the Money Principals of the venture -- one of the 0.0001%. Lovely views of the lake, famous Alpine peaks visible in the distance, clouds permitting. I had some lingering contractual obligations back in Cape Town that required me to commute between Switzerland and SA every fortnight. Business Class, naturally, at the expense of The Venture. Man, I accumulated a lot of frequent-flier miles that way.

Each time I landed back in Zurich, The Venture's minions would arrange for a taxi to schlepp me from the airport to the office. And what a taxi. Not some grotty yellow clapper with slightly sticky seats and cigarette-infused upholstery, oh no! A Mercedes limo, all leather and walnut, and Herr Geissler's pidgin English offering to take me via the scenic route as I reclined in luxury in the back watching through the blacked-tinted windows as the chocolate-box chalets went whooshing by. The trip from airport to office took around forty minutes, unless we took the scenic route.

One time, though, something went wrong, and the minions failed to arrange for Herr Geissler. Some communication breakdown. By now quite familiar with the ins-and-outs of Swiss travel, I simply took the train. There's a station right beneath your feet at Kloten airport Terminal B. To my delight I discovered that, even with a change of trains at Zurich Hauptbahnhof, the trip took only twenty-two scenic minutes by rail and Swiss chronometer, and deposited me a pleasant, three-minute walk from the office. After that I took the train whenever I was given the choice. I never commuted with Herr Giessler again, except for one last, wild time. But that's subject matter for another story altogether.

So: Driven? Yes, I've been driven. And I prefer the train, thank you.

Passionate?

Yes, I sometimes get passionate with my wife... occasionally I've been tempted to get passionate with other people. It has generally not ended well.

The thing about Passion is that it's all hot-bloodedness, sweaty palms, thumping heart, furious emotion and throbbing other bits. And bloody short-lived. Is that really what you're looking for in an Android Developer? Or would you rather hire someone who will see the project through the inevitable rough patches where your client suddenly and unreasonably invokes the corporate lawyers' jotts and tittles and throws a cast-iron spike through the limpid clarity of your gifted UX designer's heaven-inspiring vision, rewriting the spec into something dreamed up by the by-blow offspring of Dante Alighieri and H P Lovecroft on bad acid after a hard night jamming black-metal and burning Norwegian churches?

If that's really the sort of Passion you're looking for, I think I'll pass.

Imaginative?

Is there a single human-being on this Earth, of average intelligence or better, who is not imaginative? Just watch a small child persuading its parents that, No, I am NOT tired, I do NOT need to go to bed just now. Hell, even my dogs are imaginative when they're trying to persuade me to take them for a walk.

Then, too, I have been known to make claims of being a Writer (I don't say Published) of Science Fiction stories. That certainly takes some imagination. But then I had lots of practice. I learned from a Master. I went to a really strict Boys' High School, you see, along with my best mate, Roy. And Roy was one of the Naughty Boys. Constantly getting into trouble with the powers that were. More than half the time it was not even his fault and he was merely the unwitting victim of circumstance. He had some sort of genetic predisposition towards attracting trouble to himself. Consequently he became a Master at Talking His Way Out Of Trouble, and, along the way, I learned a whole lot from him about the art of fabricating stories. I learned, too, that the stories often don't need to be particularly plausible. Just good enough for the people who want to be able to pretend to believe. And that's all it takes for Science Fiction.

So: Imaginative? Yes, I think I'll own that one.

Remarkable?

I am never quite sure of the word "remarkable". Does it mean that I've done something that other people find odd enough to want to remark on, or does it mean that I am "able" to make "remarks" about odd stuff, thus being remark-able? I suspect (duh) that when you say you're looking for a Remarkable Engineer you mean the first sense -- you want someone who is "remark"-worthy. The trouble is that you don't say what they should be remarkable for... is it their dress-sense, the oddly clashing colours, pink shirt and purple neckscarf topping olive drab pants rolled up to the knees that they think make some sort of declaration of disdain for the world of conventional fashion and the sheer quantity of metalwork rivetted through the flesh of one ear so as to cause their head to lean markedly to one side? I'd certainly remark on that.

Not that I'm pointing a finger, mind. Not me. Very tolerant, I am. Peoples lifestyle and dress choices are their own, and frequently the least interesting thing about them. After all, it is pretty certain that I do or have done some things in my time that might give other people cause to remark on me. Like the sleeveless Afghan goatskin I affected back in the earliest of my student days. At least until the blackened pinhole burns multiplied like some ebonite ur-acne. That was probably remarkable. At least, the smell probably was.

So: Remarkable? Yeah, I'll 'fess up to that one, too.

Phenomenal?

Well, "phenomenal" is the adjectival form of "phenomenon", which one dictionary defines as "an appearance or immediate object of awareness in experience; a thing as it appears to and is constructed by the mind, as distinguished from a noumenon, or thing-in-itself."

I have long held the theory that the practice of software development (writing programs, to be less pretentious) is exactly the Practice of Magic. I mean, look at the Sorcerers and Wizards of fable and fantasy. (It is entirely Terry Pratchett's fault that I always want to spell Wizard with two Zs.) No, seriously, I am not even joking, here. The Sorcerers and/or Wizards (delete where not applicable) confine themselves to smoky dungeons/high garrets/dark towers, surrounded by piled-high stacks of grimoires, crafting intricate and eldritch spells in arcane and incomprehensible languages. Enchantments that, upon release into the world, wreak havoc, mayhem and general confusion. (I believe we call it "Disrupting Entrenched Monopolies".)

Remind me again: What's the difference between programming and sorcery? And Javascript really is pretty cryptic and arcane, isn't it. I wonder what DevOps opportunities there might be at Hogwarts, and do they do Agile at Mordor. (I doubt it.)

So I contend that software is all "a thing as it appears to and is constructed by the mind". By that measure, then, all software is Phenomenal. So, in using the word "phenomenal" in relation to writing code, you said nothing at all.

Seasoned?

Seasoned? What does that even mean -- a "seasoned" developer? That I've been left out in the Sun too long? That I smell a bit ripe? I don't want to know.

I can chuck some salt and pepper over myself if you like. Chilli flakes, even. MSG. Is that seasoned enough?

Finally?

Finally!

While it was not specific to any of the positions you advertised, in puffing up your dev group you invite me to "work with an absurdly talented group of people..."

I think I'll join the circus then. The Bearded Lady. The Dancing Bear. The Siamese Kittens. The Sword Swallower and the Tattooed Map Lady whose Ass Can Be Seen From Her Elba. Absurd and talented, all. Maybe they need a Scrum Master...

19 March 2015

Turning URLs into URIs: shrtn - A URL redirector/shortener.

The shutting down by Google of their project-hosting has forced me to migrate my URL-shortener project to BitBucket (because Mercurial is so much nicer than GIT), and along the way caused me to take it upon myself to resuscitate the project. For one thing, I've passed out URLS that rely on it residing at 1.mikro2nd.net, and, as things currently stand, those are dead links. For another, I've figured out a second, more important, use case for URL redirectors than mere shortening. 

While shortening URLS may have some (highly debatable) utility, I consider them to be, on balance, a harmful thing. They make the link destination opaque, and the poor sucker clicking on them has absolutely no idea where they might end up. Then, too, the indirection allows the redirector host to potential introduce any sorts of mischief or stupidity into the user's browse path. These are not insurmountable problems, just small stumbling blocks in the path of the concept, but they do argue for considering URL-shortening to be harmful. Or at least seldom accruing benefit to the clicker.

What they do buy you is the ability to gather ego-gratifying statistics on who clicked your links, which can give you a measure of how well your voice is heard in the social Internet. An oft misleading measure, to be sure, but sometimes some measure is better than none, as long as the user of said statistics remains aware of their limitations and biases.

There is, though, a very legitimate and compelling use for the idea of redirectors: They provide a bridge between the world of URIs and the world of URLs. (For the longest time I was a bit hazy on the distinction between the two, but I'm better, now, thank you.) A shortener (or redirector) service allows us to publish identifiers of stuff (I'm trying to avoid the "content" word having developed a nasty allergy) to the 'net without worrying about the location or address of that stuff. For example, I might have published a document (or an application or a collection of photographs or whatever) using some file-hosting service. Let's call it odearieme.com. Now it turns out that odearieme.com was also hosting a bunch of stuff that the American copyright fascists decide they want disappeared. So they have the FBI kick down the doors of ohdearieme's hosting centre with the aid of a compliant country's spook services, and they steal remove the servers holding your perfectly legitimate and legal "stuff". Too bad. Anybody hanging on to the URL you gave them referring to ohdearieme.com is now stock out of luck. Had you used a redirector, though, it would be no problem. All you'd have to do is upload a copy of your "stuff" to a new file hosting service ad change the destination URL in you redirector. This is, indeed, as useful thing, and, after all, exactly what they Domain Name System is all about.

So there's my compelling use-case for a URL shortener/redirector, and I still have a couple of hundred words to write to reach my day's target. Let me describe, then, some of my other thinking around this little project.

I've already implemented two or three different storage schemes for the "database" that the server needs in order to work, and exactly which one gets used for a given deployment is merely a configuration issue. So what's another one? The trouble is that it is becoming cumbersome to include all the storage implementations, along with their dependencies, in the final deployed product. I know that many, if not most, developers would just chuck everything and the bathtub into the deployment, but it offends my sense of neatness. I want to build a number of separate artefacts: one that contains the actual redirector server, and then one for each storage scheme. That way a deployer (or, heavens forfend, an actual System Administrator) can deploy only the exact artefacts they need. This also means that updates to one module don't necessitate a refresh of the entire system. More immediately, it means that I want to build a number of separate artefacts for this project, rather than a simple WAR file, making Maven a much better fit than the straightforward Ant build generated by Netbeans, so I'm having to (at last) learn something about using Maven properly. I guess I'll learn to live with the tediously slow builds, though it does feel like the 1990's called and want their Makefiles back.

Then, too, I'm of a mind to host this thing (at least my own personal redirector) on Google's massive cloudy infrastructure. Given the meagre volumes of data I'll be shifting (which, presumably, speaks volumes for the paucity of my Social Internet Fu) I'm pretty-well certain I can keep hosting it there free for all eternity—or until Google decide to shut down their cloudy hosting engines or make them for-pay only. If or when that happens I guess I'll have to move the redirector back onto my own infrastructure, so I don't want to lose the ability to deploy to my own (Tomcat) application server. That means teasing out the deployment configuration and infrastructure-specific stuff from the core of the redirector code itself. All sounds reasonably doable for me, and I'm using the exercise to polish up my Mercurial, Maven and AppEngine skills, not to mention brushing up on changes in the Java language and APIs. I might even use it to improve my Javascript skills or get properly to grips with one of the JS front-end frameworks.

04 September 2013

Darker Corners of Android: BroadcastReceivers and Intents

Not all in Android-land is lightness and brightness. A few dark corners and cul de sacs await the unwary, ready to trap us in sticky tarbaby snares of mystery debugging. One such is a case we recently encountered in relation to BroadcastReceivers and the way Intents get delivered to them.

I've written previously about implicit Intents versus explicit Intents in the context of describing a useful code-pattern for preparing Intents. To briefly recap, explicit Intents name the class of the component they are aimed at whilst implicit Intents simply describe the desired action they intend to trigger and allow the Android intent-delivery system to find the best-matching component to handle the Intent. Indeed, a better naming scheme might have been "broadcast Intent" for implicit Intents and "unicast Intent" for explicit Intents.

BroadcastReceivers can be registered for receiving broadcast Intents in one of two ways, either through a declaration in the Android manifest file, or via a dynamic registration by another component.

If you register a BroadcastReceiver via a manifest declaration, then you must provide the class-name of the receiver implementation. To communicate with the receiver, you broadcast an Intent – either one that matches the receiver's intent-filter (if it was declared to have one) or an Intent that specifies the class-name of the receiver. In this latter case it's hardly a broadcast, really, since the Intent is directed at a specific target receiver. Nevertheless, this is a perfectly valid way to send events and information to a BroadcastReceiver.

If you dynamically register a BroadcastReceiver – by calling Context.registerReceiver() – you supply a BroadcastReceiver instance and an IntentFilter describing the sorts of Intents that should be passed to the receiver. And herein lies a small gotcha. You register an instance.

If you try to send an Intent to the receiver by naming the class of the BroadcastReceiver, it will never get delivered. Sending the Intent using something like

Intent i = new Intent( this, MessageReceiver.class );
sendBroadcast( i );

won't work. The Android system will not match the Intent you declared to the class of the BroadcastReceiver instance you registered. The only way to send Intents to dynamically registered BroadcastReceivers is by having them match the IntentFilter attached to the receiver when it gets registered.

Explicit Intent matching just doesn't work in this case.

I've put some example code on github that demonstrates all this. There's a simple BroadcastReceiver (the MessageReceiver class) that gets registered dynamically by the MainActivity. The MainActivity instance then tries to send Intents to this receiver instance using two different methods. First it sends an Intent that matches the Action declared in the receiver's intent-filter. That Intent gets through successfully. Then it tries to send an Intent that explicitly names the MessageReceiver class. It never arrives.

As much as I love the eventful nature of Android software architecture I view this as a bug (Come on guys... How hard would it be to do a getClass() on the instance that gets registered?) but it is what it is. It took us a day or so to figure out why Intents were mysteriously failing to be delivered to an otherwise perfectly ordinary BroadcastReceiver, so I thought I'd save you, Dear Reader, the trouble. It's pretty straightforward to cope with (rely on Intent actions rather than explicit Intents) once you know of this issue, but I have not seen it documented anywhere in the Android reference materialsε.

Share and Enjoy.

ε If anyone does spot a description of this in the official docs, please do let me know!

27 July 2013

Android Patterns: Manufacturing Intents

Activities, Services and BroadcastListeners are the basic components of application structure in Android. They are all started by firing an Intent that causes the Android system to activate them. There are two very different kinds of Intent that might activate an Activity: implicit Intents, and explicit Intents.

We present here a useful pattern for creating those Intents in a way that makes code more robust and easier to maintain.

For the purpose of simpler description we'll talk about starting Activities using Intents, though the same pattern makes most sense when used (with due care and discretion) for all components.

Implicit Intents


Implicit Intents do not name the specific Activity they trigger. Instead Android looks for the best match between an Intent's content and any Intent Filters declared by the Activity. This is what happens when your application's main Activity is started by an Intent that has its action set to ACTION_MAIN and its category set to CATEGORY_LAUNCHER.

Implicit Intents and their matching by IntentFilters is the key way that applications are able to interact with the entire ecosystem of applications and services in an Android system. It allows us to run an application that (for example) makes use of a web-browser or email client without having to embed all the functionality of a browser or email client in the application itself; we can merely assume that a suitable application exists in the ecosystem, and all our application has to do to leverage that capability is to fire the right Intent. This system of implicit Intents is one of the most powerful aspect of Android, and one which many applications fail to use well. How many applications have we seen that try to provide their own web-browsing capability, ending up providing some half-baked, crippled web-page fetcher-and-renderer, while we know perfectly well that the system almost certainly hosts one or more full-fledged, powerful web-browsers, if only they would just fire an appropriate Intent instead of trying to go it alone.

Explicit Intents


Explicit Intents are Intents that name a specific component – an Activity, Service or BroadcastReceiver – as the thing to be activated using the components class name. As soon as an Intent names a specific component, all other forms of Intent matching against filters is abandoned, and Android activates only the specific component, without any regard for the other bits of data the Intent may carry.

In terms of Activities, explicit Intents are used to implement in-application navigation between Activities, and many of the target Activities will not have any associated IntentFilter at all, so there is no other way to activate them other than with explicit Intents fired form other parts of the application.

Pattern


Each Activity, Service or BroadcastReceiver should provide a factory that returns template Intents for starting that Activity, Service or BroadcastReceiver. In the simplest case – say an Activity started by an explicit Intent – this might be a simple factory method:


public class ListManagerActivity extends Activity {

    public static Intent getStartIntent( Context context ){
        return new Intent( context, ListManagerActivity.class );
    }
    ...
}

In this most elementary form such a factory method is not, by itself, very useful. It becomes ''much'' more compelling when the Activity in question expects or requires certain additional data to be present in its starter Intent:


public class ListManagerActivity extends Activity {
  public static Intent getStartIntent( Context context, UserInfo userData ){
    checkNotNull( userData );
    final Intent startIntent = new Intent( context, ListManagerActivity.class );
    startIntent.putExtra( EXTRA_USERINFO, userData );
    return startIntent;
  }
  ...
}

In this case we achieve two worthy objectives with this simple factory method:

  1. We make sure that the Intent that starts the ListManagerActivity always contains the userInfo extra that (presumably) the activity absolutely requires in order to behave correctly, provided, of course, that we only ever manufacture start Intents for ListManagerActivity using this method, and never by calling new Intent(...) – something that can quite easily be checked using various code-quality audit tools.
  2. We keep the code that builds start Intents (with their extra data) close to the code that consumes those Intents (and that extra data) so that, if we change the notion of what data an Activity requires, we're already in the right place in the code to make the corresponding fixes to the manufacture of those Intents, rather than having to hunt around all over our codebase.


There might be more complex cases where components get started using a variety of different Intents. Perhaps Intents might carry different extra objects or flags affecting the component's behaviour. Perhaps the component might be activates using implicit intents with varying options for their data URI. Indeed, a common mistake when passing a data URI in an implicit Intent is forgetting to correctly set the mime-type for the data URI, in which case matching the Intent against an IntentFilter mysteriously fails.

In such complex cases it may be appropriate to supply several Intent-factory methods in the class that gets activated. It may even be desirable to provide an Intent Builder class:


public class ListManagerActivity extends Activity {
    public static class Builder {
        private Intent startIntent;
        public Builder( Context context, Foo mandatoryData ){
            startIntent = new Intent( ListManagerActivity.class );
            startIntent.addExtra( EXTRA_FOO, mandatoryData );
            startIntent.addFlags( FLAG_ALLOW_NEW_ITEMS );
        }
        public Builder addVitamins( final Vitamin nutrient ){
            startIntent.addExtra( EXTRA_VITAMIN_ID, nutrient.getId() );
            return this;
        }
        ...
        public Intent build(){
            validateIntent();
            final Intent constructedIntent = startIntent;
            startIntent = null;
            return constructedIntent;
    }
}

TL;DR:


Don't create start Intents for Activities, Services and BroadcastListeners willy-nilly in myriad and random places all over your codebase. Rather have each such component class provide factory methods or builders to manufacture those start Intents.

This ensures that
  • the Intents carry all the necessary data that the component expects, 
  • that the data is added in close proximity to the places where it is consumed, so that changes in the requirement are easily mirrored in the corresponding changes in the creation and structuring of that data, and
  • mandatory data required by the component can be reflected by mandatory arguments in the signature of factory methods, ensuring that required data cannot be easily forgotten.

21 May 2013

Netbeans EE Dependency Madness

Dredging up an ancient persona-time web-project and dusting it off caused me to reinstall the Base-EE module for Netbeans - long my IDE of choice. To my dismay, Netbeans pops up the following list of dependent modules:
This is madness!

I will never be using JSF, IceFaces, PrimeFaces or Struts. There will be frost on the ground in Hades before I ever use any part of Spring Framework without being severely and repeatedly bludgeoned with a blunt instrument. And if I ever do choose to host a webapp "in the cloud", Oracle is probably going to be my very last choice, though I can understand them being desperate to punt their wares in an IDE that they do, after all, fund in some measure.

So why are these listed as required dependencies? It might be that the EE Base module uses some features from those modules, in which case, why have those not been broken out as separate modules, this being one of the huge strengths of the Netbeans Platform?

Something is very smelly in the nation of Netbeans!

29 November 2012

Web-design HowNotTo

Seen recently on a mailing-list I receive:

> These guys stock 29mm Crown Caps http://www.africancork.co.za


Went off to take a look at their website...



"You have to be logged in to view our products"


They have to be joking! I can only assume that they actively hate it when people want to buy their stuff. Needless to say, I bounced.


Do people really still misunderstand the nature of the web that badly when we're already more than 10% of the way through the 21st Century?

05 November 2012

Google Groups: The Orphan Child

It's been quite a while, now, since Google rearranged the menus providing links to their various sites and tools. And right from the start Google Groups has been Missing In Action. At first I thought it might just be a small oversight, but evidently it's a deliberate design decision.
Google Groups: Nowhere to be found.

I wonder why... I find Google Groups to be one of the most useful tools they offer, especially when I am working with people who are not very net-savvy -- those who are not as comfortable with web-based tools as I am. The only rationale I can come up with is that Google would like us all to migrate over to g+ and abandon the somewhat old-school "forum" style that is Groups. Ain't gonna happen!

Unless they force the issue by killing-off Groups, in which case I will hazard a guess that existing fora using Google Groups as their carrier will simply hike on over to elsewhere that does provide a forum-style interface.

I'm not a huge fan of forum-style UX, and I avoid it as far as possible, but it is something simple and familiar to many people, and so sweetly occupies a place where it is the best thing to use because it is the simplest thing for the largest number of users. I do hope it's not going to vanish completely in a stupid attempt to force everyone over to g+... (Like what Microsoft seem to be trying to do to their user-base.) That would be,... well, not quite Evil, I guess,... but certainly quite an unfriendly move.

29 October 2012

Unit-testing Android Apps: Netbeans and Robolectric

Testing for Android Apps is a bit of a mess. The Android Test Framework dismally fails to make a clear distinction between unit tests, functional/integration tests and UI tests, and assumes that all tests will be run as an Android application on a device or emulator (AVD) as Dalvik code. For unit testing this is extremely clunky. It means that unit tests must be written as a standalone Android project, built as an APK and deployed to the device running a build of the application under test, where it takes over the running of the application to instrument it for testing. This is quite a slow process, and slowness is the very opposite of what we want for unit tests.

All I wanted, though, was to run (quickly!) some simple unit tests against domain classes. There might be some Android mocks needed where my application classes rub up against the SDK, but that's just accidental.

Then, too, there is (currently) a bug in the Ant build script for the Android Test Framework that means that, out of the box, tests cannot be built or run using Ant. I neither know, nor do I care, whether the test might be runnable if I were using Eclipse; I'm a diehard Netbeans user, and Netbeans uses Ant (or Maven, and possibly soon, Gradle) rather than relying on a parallel build system of its own. I think this is one of Netbeans's key strengths. Along with the still-evolving NBAndroid Module and Netbeans's superb out-of-the-box handling of XML files, I've been finding it an absolute pleasure to use for Android development. But unit-testing remained an itch that I had to scratch.

The upshot of all this is that I could not find a satisfactory -- or, indeed, a working -- way to write and run unit tests for the Android project I'm working on.

Down into a Rabbit Hole I went!

Emerging a couple of days later, here's the solution I came up with... It's probably not the best solution ever. It's certainly not definitive. But it Works For Me.

I'm using Robolectric -- a framework that modifies Android platform methods on the fly and provides shadow classes for Android platform classes so that unit test can be run in a standard JVM on my development machine.

Predictably, it was not all Plain Sailing!

The Robolectric documentation is pretty sparse, and very strongly oriented towards IntelliJ and/or Maven-and-Eclipse usage (didn't investigate too closely, don't care!) If I'm going to be shafted into using Maven, then my unit testing is so slow that I might just as well stick with using the standard Android Way of Testing, so I wanted to avoid that. Additionally, this brings the advantage that I can use JUnit-4 style tests (with a caveat - see below.)

The first step is to create a standard Java Project in Netbeans. We already have a directory structure like

...project-root
       |
       +---android-main-project
       |
       +---other-project-related-dirs

so I created my unit-test project on the same level as the main project:


...project-root
       |
       +---android-main-project
       |
       +---other-project-related-dirs
       |
       +---unit-test-project


In Netbeans add at least the following libraries as dependencies for your test project:

  • Your main project's bin/classes directory - otherwise the test-project cannot find the classes you want to test.
  • JUnit
  • Any mocking library you might choose to use - PowerMock looks pretty good, though I haven't used it beyond tyre-kicking yet.
  • android.jar for the SDK version you have as your targetSdkVersion in the AndroidManifest of your main project. This is important, since the Robolectric framework uses a custom test-runner (which we'll slightly modify as described below) that examines your AndroidManifest to figure out where to find resources.


The order you specify the libraries is critical: android.jar must come after junit in the classpath for your unit-tests.

Now only one more things remains: telling Robolectric where to find your main project's manifest file and resources...

In your test-project, create a custom test-runner like

net.mikro2nd.android.junit;

import java.io.File;

import org.junit.runners.model.*;
import com.xtremelabs.robolectric.RobolectricConfig;
import com.xtremelabs.robolectric.RobolectricTestRunner;

public class MyTestRunner extends RobolectricTestRunner {
    public MyTestRunner( final Class<?> testClass )
        throws InitializationError
    {
        super( testClass, new RobolectricConfig(
            new File( "../android-main-project" )
        ));
    }
}


Of course you should now annotate your test classes using

    @RunWith( MyTestRunner.class )
    public class DeviceIdTest{
        ...
    }


Now you can "Test" your test project to run all unit tests, select just a single unit-test class to run, or even debug unit-tests without leaving Netbeans at all. And you can run them in a CI server.

Slightly less convenient than unit-testing a non-Android project, but still way better than the alternatives (no testing, Eclipse or Maven, or the bloody slow Android Test Framework.)

My thanks to Justin Schultz who's Eclipse Robolectric Example put me on the right track. All I've done is adapt his hard work for Netbeans use.

11 August 2012

Moving Furniture: The Scrum Way

As a housewife, my wife
Wants the bed in one room swapped with the couch/futon/bed in another
So that  she can more comfortably sit and watch the Olympics on TV in the sunnier, warmer room.

With 3 programmers in the room, it was inevitable that we would choose an Agile approach to this requirement. The User Story above was our first step, and was duly agreed to by said wife as Accurately Describing What She Wanted and its Business Value.

Given the constraint that both rooms involved are too small to contain both pieces of furniture simultaneously, we realised that we would need a temporary variable as a swap space. I'm pretty sure that, barring some major advance in our understanding of Quantum Mechanics, pieces of furniture can't be swapped by being XOR'd through each other.

We broke the Story down into some Tasks:

  1. Remove the bed from Room 1 into the Swap Space.
  2. Remove the Futon from Room 2 into the Swap Space.
  3. Move the Futon Base (a finger-eating piece of folding woodworkery) from Room 2 into Room 1 and position it in its final position.
  4. Move the Futon from the Swap Space into Room 1 and position it on its Wooden Base.
  5. Move the bed base from the Swap Space into Room 2 and position it in its final position.
  6. Move the mattress from the Swap Space into Room 2 and place it on its base.

Seemed like a reasonable breakdown at the time. We did some estimations and figured we could fit it all into a single Sprint.

Here's how it played...

Task-1 went quite smoothly. Mattress and bed base moved to the Swap Space.
Task-2 also went well. Please bear in mind that the futon and mattress are large, floppy and heavy items, so not all that easy to move about.
Martin and I -- Pair Moving -- make an attempt at Task-3. Space is very constrained, so moving the Futon-base into Room 1 involves a brief detour into Room 3. Halfway there we suddenly realise that -- because the base is asymmetric -- we have to turn it around in the Swap Space before it can be Deployed into Production.
We complete Task-4, only to realise that we were entirely wrong about which way round the base has to go. We've moved it into the room the wrong way round. And the room is too small for us to turn it around short of Interesting Topological Manoeuvres.

So we back out again, via Room 3, back into the Swap Space where there is sufficient room to turn the base around.
Some discussion ensues. We realise that we should have had better Unit Tests in place for this Task. Finally we manage to complete the Task.
It's a tight fit, but the Futon/couch will serve our User perfectly as she wished.
Sadly there's an unforeseen bug. A small drawer unit now blocks the doorway, making for extremely poor UX. Not our problem, though. We've met our spec and implemented the Story. People will simply have to leap over the drawer-unit for the time being. Changes will have to be the subject of a subsequent Sprint.

Happily I can report that the rest of the Sprint went quite as anticipated. We were even able to implement a small Improvement Task by orienting the bed in Room 2 in a much more User Friendly fashion.

I do wish Furniture Topology was amenable to Version Control, though. It would have made our Task-4 issues a bit simpler to resolve. But then, perhaps we should have abandoned the Sprint at that point.

Still not quite sure how all the Scrum ceremony helped, though.

03 April 2012

JSP/JSTL: Versions and Standards

It baffles me. So many developers who use  infrastructure standards like servlets and JSP, but don't have any idea what versions of these they're coding to. They are blissfully unaware of whether they're targetting HTML4.01, XHTML-1 or HTML-5, CSS 2.1 or CSS 3, Servlet 3, 2.5 or 2.4...

Maybe I'm just funny that way - I think that managing dependencies in a tight, controlled way really matters! Otherwise things quickly spiral out of control, and you end up with that classic Design Pattern, "Big Ball of Mud".

On the other hand, I freely confess that I keep forgetting which pieces belong together. It's such a confusopoly.

So, mainly as a Note To Self, here's a quick lookup:

JSP/Servlet/JDK Versions

Servlets JSP JSTL Java EE Tomcat Min. JDK
3.02.21.2 57.x1.6
2.52.11.2 56.x1.5
2.42.01.1 1.45.5.x1.4

The list goes on, but if you still have systems in production running those archaic versions, you're probably in deeper trouble that mere version-confusion anyway!

06 February 2012

Work Wanted

This is a small call for help. I am looking for work, and need your help.

Contract or permanent. Preferably (but not exclusively or even at all) telecommute. I can code, design, architect software, consult in any of these (and dev process/team issues) and teach a variety of Java, OO Design and web development topics, and would be happy to do any/all of these. I additionally have some experience doing system administration work. I work in Linux/Unix environments and know next-to-nothing about Windows.

I've done web stuff, and loads of backend "heavy lifting" coding where reliability, scalability, etc. are important. I am not good at quick'n'dirty. Skeptical of the value of buzzwords and big-arse frameworks.

Check out my "business" website for more details about me and what I've done in the past.

R (as they say) "highly neg".

If you have anything suitable, or know of anything that fits, please drop me a line.

I will be in Cape Town and environs next week, so an ideal time to get together and chat about possibilities and opportunities.
Related Posts Plugin for WordPress, Blogger...