Posts Tagged ‘BlackBerry BUnit UnitTesting’

Unit-Testing BlackBerry Applications

December 14, 2010

I’ve been trying to find an easy way to unit test BlackBerry (BB) applications for a while. It is a little challenging for different reasons (e.g., several network resource dependencies.) Nonetheless, there’s some hope, as described in this post. If you design your app with SOLID principles, your core areas will become easier to unit test.

There are a few frameworks that can help you unit-test your BB app; one of them is BUnit. This post describes how to use this framework to unit-test a simple BB Hello World app. Hopefully this will make it easier for some people to add automated tests to their BB apps.

Assumptions

You should have Java, Eclipse, and BB’s 4.5.0 JDK plugin already installed on your development environment (although it should work with different versions). You should also be familiar with basic BB development concepts.

Design

This is a simple HelloWorld app (nothing too fancy here). Below is its sequence diagram. Our unit test will be focusing on the HelloWorldRepository class. If you want to look at the final code source (including the unit test), you can download the source at git://github.com/iterativo/Blackberry-Hello-World.git

HelloWorldSequenceDiagram

Adding BUnit to your project

At this point, I’m assuming you’ve already created a BB project in Eclipse. After that, the first thing you’ll need to do is download BUnit (if you haven’t yet), unzip it and just copy the contents in src/jmunit into a new package folder in your BB project called test. This is what your project structure should look like so far:

ProjectStructure_1

Test-driving the Implementation

Let’s also assume that you’ve already implemented the HelloWorld and HelloWorldScreen classes in the sequence diagram. The HelloWorldScreen class is making a call to an unimplemented method in HelloWorldRepository. This is what HelloWorldRepository looks like so far:

public class HelloWorldRepository
{
	public String getMessage()
	{
		return null;
	}
}

Now let’s say you’re a good boy/girl and you buy into the whole TDD idea. So before attempting to implement HelloWorldRepository, you want to create a unit test. This is where the fun begins.

So let’s add the first unit test on you test package folder and let’s call the class HelloWorldRepositoryTest. It will have one test for HelloWorldRepository’s getMessage() and another method to hook up a test runner (more on that later). Here’s what the test class looks like:

public class HelloWorldRepositoryTest
{
	public void testShouldGetHelloWorldMessage() throws AssertionFailedException
	{
		HelloWorldRepository repository = new HelloWorldRepository();
		String message = repository.getMessage();
		Assertion.assertEquals("ShouldGetHelloWorldMessage", "Hello World", message);
	}

	public void test(int testNumber) throws Throwable
	{
		switch(testNumber)
		{
			case 0: testShouldGetHelloWorldMessage(); break;
			default: break;
		}
	}
}

Before we can run this test, we’ll need to do 3 more things.

  1. Add an alternate entry point to our HelloWorld project to be able to run our unit tests as a separate app.
  2. Add a way to report the results.
  3. Add a Test Runner, which will call our test(s).

Let’s do this.

Adding an alternate entry point for our tests

You could implement a different way to call the unit tests, but I prefer to have a separate app icon on your phone that launches the tests. This is why I added an alternate entry point for this example. If you use this approach in your project, you’ll obviously need to make sure to not include this on your deployment code, but I’ll leave that to you to figure out. In the meantime, this is what your main() method will look like after adding the new entry point:

public static void main(String[] args)
{
	boolean startApp = false;
	boolean runTests = false;

	for (int i = 0; i < args.length; ++i)
	{
		if (args[i].equals("helloworld")) startApp = true;
		if (args[i].equals("helloworldtest")) runTests = true; // name of the alternate entry point for your tests app
	}

	if (startApp)
	{
	    	HelloWorld theApp = new HelloWorld();
	    	theApp.enterEventDispatcher();
    	}

	if (runTests)
	{
		HelloWorldTestRunner testRunner = new HelloWorldTestRunner();
		testRunner.enterEventDispatcher();
	}
}

Make sure to make the corresponding changes to BlackBerry_App_Descriptor.xml as well. Notice that this new entry point is calling our test runner. Let’s create it.

Adding a Test Runner

This is the class that will be called when the test app launches (through the alternate entry point). Its responsibility is to run the unit tests and delegate the test result reporting to the corresponding class. Here’s what it looks like for our example:

public class HelloWorldTestRunner extends UiApplication
{
	public HelloWorldTestRunner()
	{
		HelloWorldRepositoryTest repositoryTest = new HelloWorldRepositoryTest();
		try
		{
			repositoryTest.test(0);
       		} catch (Throwable ex) {
            		System.out.println(" ---> "; + ex.toString());
            		ex.printStackTrace();
       		}

       		pushScreen(new UnitTestResultsScreen());
	}
}

Now the last step is to go ahead and create the test result reporting class.

Adding a Test Result Reporter

Straight up from the BUnit example (included in the downloaded zip file), just copy/paste the UnitTestResultScreen class in your test package folder. This is basically the screen that will show up with the test results summary.

Running the Test

At this point you should be able to compile/launch your project using the BB simulator. Once your simulator is up, you should see a new icon to launch your tests – it will have the name you gave it when you defined the alternate entry point on your Blackberry_App_Descriptor.xml. Launch it and you should get this screen:

failingTest

Great! You have a failing test with a descriptive message. You may be thinking whaaaat? Well, remember we’re doing TDD. You’re supposed to get a failing test first. Now let’s make it pass.

Making the Test Pass

To do this, let’s fix HelloWorldRepository.getMessage(). Here it is:

public class HelloWorldRepository
{
	public String getMessage()
	{
		return "Hello World";
	}
}

And let’s run the tests again. This time you should see your test pass:

passingTest

Voilà!

Going Forward

If you’re used to JUnit (or any XUnit framework for that fact), you may have noticed we did some things manually (like wiring up the test to the runner). Unfortunately, BB is based on J2ME, which doesn’t support Reflection, so you have to take care of managing the wiring. In our case that means you’ll have to update the TestRunner with any new tests you add. That’s a price you should be willing to pay, as the benefits unit-testing brings you can let you sleep really well at night (especially when your project starts growing).


Follow

Get every new post delivered to your Inbox.