Articles Feed

Authors

Categories

Up and running with TDD on Android

by: colin | July 11th, 2009 |

A couple of weeks ago, I happened to be in the right place at the right time (the first ORD Session) when Google hooked a bunch of developers up with an Android Dev Phone 1. I'd been interested in Android for awhile because it's a more open platform than the iPhone, and the code is Java (I've never worked in Objective-C), so I was excited. After my initial excitement of hacking around and getting things to work, I decided to regain my discipline and figure out a workflow for TDD. The good news is that JUnit is built right into the Java framework that Android app have available. The less-good news is that writing and running tests on Android isn't as well-documented as many other facets of the application framework. I'd like to share my setup, which doesn't depend on any specific IDE or text editor. I'm assuming Mac or Linux here, but I'm sure Windows would only require minimal changes. Other assumptions: Note: the "tools" directory is the main one, not the one below "platforms" - in my case: '/Users/colin/lib/android/tools' Let's get started by creating a project with the command-line utility, which will give you the test directory structure and build files that you need (you won't get these with the Eclipse or IntelliJ plugins, for instance). Of course, you can read documentation for the android command to see more details, but here's what we're doing:
  1. $ android create project -t 1 -p tictactoe-android -a TicTacToe -k com.colinwjones
  2. $ cd tictactoe-android
All of these options are required (-n, the project name, is optional, and I've left it out above) Next, we want to create an AVD (Android Virtual Device) if one doesn't already exist - it's an emulator for the phone operating system. We can check to see if one exists already first:
  1. $ android list avd
If you don't have an AVD already, create one:
  1. $ android create avd -n ColinPhone -t 1
You'll be asked if you want a custom hardware profile (I don't, so I just hit Return). At this point, let's go ahead and fire up the emulator, making sure to match the name to the one you created. It's good to make sure that our state at this early point is a good one.
  1. $ emulator -avd ColinPhone &
Here I'm launching the emulator (AVD) in the background so I don't need to open up a new Terminal window. You'll get some output in your terminal window; if it bothers you, Ctrl-L will freshen it up. Now we'll build and install both of your apps (the test package is really a separate application, which is a good idea to keep the tests out of the eventual deployment package anyway).
  1. $ ant debug
  2. $ adb install -r bin/TicTacToe-debug.apk
  3. $ cd tests
  4. $ ant debug
  5. $ adb install -r bin/TicTacToe-debug.apk
The debug target isn't actually defined in either buildfile (build.xml or tests/build.xml), but is available nonetheless (see $ ant help for other targets you might not otherwise find). This takes care of bundling resources, compiling, converting classfiles to Android's .dex format, and packaging. Note that adb is NOT ant - it's the Android Debug Bridge, and it's invaluable for working with the emulator. The -r option to adb install reinstalls the package if necessary. Now, this is pretty redundant-looking, but just remember that the tests directory is a sort of parallel structure with your project directory, and you need both. It's time to run the default test suite that the android create project call has given us (this can be run either from tests directory or from the root of the project):
  1. $ adb shell am instrument -w com.colinwjones.tests/android.test.InstrumentationTestRunner
Of course, you'll want to substitute your package and activity names for the ones in my examples. It's very important to realize that you've just compiled and installed your software on the emulator and are running tests on it there. In order to do TDD, you'll need to recompile changed code and reinstall on the emulator. I hope that one day there will be a way to avoid going through the emulator each time (or that there already is one that I've been unable to find), but this is the only method I've been able to get working so far. Now we need to actually add a real test to (in my case) tests/src/com/colinwjones/TicTacToeTest.java. Here, an IDE like IntelliJ or Eclipse comes in handy, especially if you're just starting with Android and aren't sure of the methods you might want to use.
  1. // with imports at top:
  2. import import android.widget.Button;
  3. /* some code
  4. * ...
  5. * ...
  6. */
  7. // inside your test class:
  8. public void testNewGameButtonExists() throws InterruptedException
  9. {
  10. Button button = (Button) getActivity().findViewById(R.id.new_button);
  11. assertEquals("New Game", button.getText());
  12. }
Building our test package at this point will fail, since no resource with the new_button id exists yet. Let's do it anyway to see the first failure and guide us to our implementation code (running this from the tests directory):
  1. $ ant debug
The error tells us where to go next: we'll implement the button in /layout/main.xml (making sure to set the right ID on the button). Since we changed the main layout, the implementation package needs to be built first, then the test package:
  1. $ cd ..
  2. $ ant debug && adb install -r bin/TicTacToe-debug.apk
  3. $ cd tests
  4. $ ant debug && adb install -r bin/TicTacToe-debug.apk
Now run the tests again:
  1. $ adb shell am instrument -w com.colinwjones.tests/android.test.InstrumentationTestRunner
Great! Now we have a proper failure:
  1. com.colinwjones.TicTacToeTest:
  2. Failure in testNewGameButtonExists:
  3. junit.framework.AssertionFailedError: expected:<new game> but was:<>
We just need to add the right text in the XML layout: Let's rid of the default "Hello, World" TextView in main.xml while we're at it. Now rebuild, reinstall, and re-run tests
  1. $ cd ..
  2. $ ant debug && adb install -r bin/TicTacToe-debug.apk
  3. $ cd tests
  4. $ ant debug && adb install -r bin/TicTacToe-debug.apk
  5. $ adb shell am instrument -w com.colinwjones.tests/android.test.InstrumentationTestRunner
This is getting annoying typing all these commands: we're going to want to write a shell script or Ant task to do this for us. But for the time being, we'll plod through (that was the last time, though). Now we're passing:
  1. Test results for InstrumentationTestRunner=..
  2. Time: 1.009
  3. OK (2 tests)
It's a bit strange that the test runner claims we have 2 tests: each test class will add one of its own. Now that we've seen how to get the tests running, let's automate it by adding Ant tasks to the build.xml in the main project directory. You'll need to set the environment variable ANDROID_TOOLS for this exact task to work, or you can provide the full path to adb.
  1. <target name="clean">
  2. <delete includeemptydirs="true">
  3. <fileset dir="bin" includes="**/*" />
  4. <fileset dir="tests/bin" includes="**/*" />
  5. </delete>
  6. </target>
  7. <target depends="clean, debug" name="test">
  8. <property environment="env" />
  9. <property name="android-tools" value="${env.ANDROID_TOOLS}" />
  10. <ant dir="tests" antfile="build.xml" inheritall="false" target="debug" />
  11. <exec executable="${android-tools}/adb" failonerror="true">
  12. <arg line="install -r bin/TicTacToe-debug.apk" />
  13. </exec>
  14. <exec executable="${android-tools}/adb" failonerror="true">
  15. <arg line="install -r tests/bin/TicTacToe-debug.apk" />
  16. </exec>
  17. <exec executable="${android-tools}/adb">
  18. <arg line="shell am instrument" />
  19. <arg value="-w" />
  20. <arg line="com.colinwjones.tests/android.test.InstrumentationTestRunner" />
  21. </exec>
  22. </target>
Now we can just run
  1. $ ant test
from the project directory (assuming the emulator is already up and running with $ emulator -avd ColinPhone &), and we'll be good to go. Honestly, it's a pretty simple process: the key for me was in using the Android command-line tools rather than IDE plugins. It helped me to understand the build process and get beyond the initial frustration of not having the IDE do the work for me. I imagine things will change a bit for Windows users, so please leave comments if there's anything drastically different (and also if things change for Mac/Linux users as the framework develops). I do hope this will help someone else to get set up and save the headache I had when first discovering Android.

2 Responses to “Up and running with TDD on Android”

  1. Fred Grott Says:

    Yo have errors in techinque..

    Always fork the cal of the other ant script with the -f arg..

  2. karthik Says:

    send the code of tic tac toe

Leave a Reply

#