Java ME Testing Done Right

Is there a right or wrong way to unit test applications? We believe so. Especially when it comes to testing Java ME applications. Down-scaled copies of our faithful testing frameworks are popping up everywhere in the Java ME community. But has anyone dared to ask the question: Why?

The Problem

Java Micro Edition (Java ME) is the most ubiquitous application platform for mobile devices. However, testing Java ME applications is not a simple task. The constraints on the Virtual Machine (VM), the limited resources and the not-always-so-well-designed APIs are all contributors to this statement. The limited environment has ”forced” application developers to write their code as compact as possible. On many an occasion this has led to an unsound application design that isn’t test friendly. In a test friendly design, the collaborators of an object can easily be stubbed. Such a design is often called a testable design. There are architectural patterns such as dependency injection that can be used to achieve a testable design. Even though this is mostly used in enterprise applications, it can be used with most modern programming languages and environments.
The ability to stub collaborating objects is great when testing code. We can create
fake or ”mock” instances of our collaborating objects and set up an expected behav-
ior for them. By doing this, we can isolate the code being exercised and verify that
the behavior of the collaborating objects was met, i.e. verify that the code under test
actually did what we intended it to do. This is known as unit testing based on mock
objects. Those who are inexperienced with this kind of testing can fi nd some good
articles on the subject in the References section located at the end of this article.
In the Java SE world, there are excellent tools like JUnit and EasyMock for creat-
ing unit tests based on mock objects. Since Java ME doesn’t support refl ection, we
have lighter versions of JUnit, such as J2MEUnit, JMUnit and Mobile JUnit. As the
Connected Limited Device Confi guration (CLDC) VM doesn’t support dynamic
object creation, we can’t use EasyMock and have to write the mock objects our-
selves.
Even if you have a testable design, there are more issues that make unit testing dif-
fi cult in Java ME:

  • Creating mock objects is tedious and error prone. There are tools that can help
    you generate code for your mock objects, but you have to regenerate these if you
    change your objects, otherwise you may encounter version confl icts. This is why
    dynamic frameworks like EasyMock have gained such popularity.

  • Running unit tests on the target platform takes time. Even if it only takes a minute
    to install and run some tests, it may be enough to make you not want to run the
    tests every time you change your code. Running JUnit tests in Eclipse takes a cou-
    ple of seconds. Furthermore, the limited environment puts a constraint on how
    many tests you can run on your Java ME device. This forces you to split your test
    cases between several test suites, creating even longer turn-around times.

  • Automation of tests is diffi cult. You’ll need a physical device attached to your
    build server to run your tests.

  • The test suite setup is done manually with Java ME unit testing frameworks. Each
    time you add or remove a test, you have to update your test suite setup. This takes
    unnecessary time and is error prone.

These issues lead the Java ME developers to the darker side of testing: integration
unit testing. Integration unit testing means that you don’t fully isolate your unit un-
der test, but rather let it talk to real instances of all or some of its collaborators. There
are however some problems with integration unit testing: It catches errors too late in
the development process, it’s complex to set up, and it often causes seemingly end-
less problems when trying to manage the test data. Unit tests based on mock objects,
on the other hand, require a minimum of test data, they are written together with
your production code, and they verify that your code actually does what you want it
to do. Java SE has elegant solutions to all of the above issues. Why not use them?

Introducing MockME

For Java ME developers, there is an underlying assumption that unit tests need to be
run on the target device. This is not true. Consider a Java EE project: An enterprise
application may be targeted to run on an application server on other hardware and
JVM vendor from our development environment. The unit tests are still run in
the development environment. How does it work? The ability to ”write once, run
everywhere” is one of the greatest strengths of Java. However, any experienced Java
developer knows that this is not completely true. There can be some compatibility
issues (e.g. threading), but for unit tests this will not be a problem. We’re only testing
a small unit in a controlled setup.
Given that the above statement is true, we could run our tests in a Java SE en-
vironment and utilize all its excellent tools! This is the basic idea behind MockME;
to write unit tests for Java ME applications and run them in Java SE. So how does
it work? MockME contains empty objects for CLDC, Mobile Information Device
Profi le (MIDP), and other JSRs designed to run in Java SE. Then we use EasyMock
to create mock objects dynamically from MockME. Now, some might ask: ”Why
not create the mock objects from the classes in a wireless toolkit (WTK)”? Unfor-
tunately, these toolkits contain native methods that can’t be mocked. Even if it was
possible, EasyMock can’t mock static methods, such as RecordStore.openRecordS
tore(String,boolean). MockME solves this by delegating static method invocations
to a static delegate instance with a special method name. Let’s have a look at the
inner workings:

RecordStore.java

By creating a mock object and setting the static instance, we fake the static method
invocations. No real magic here, but it’s important to make the development of Java
ME unit tests much simpler.

MockME In Action

OK, after motivating MockME, let’s look at a small example:

MessageDao.java

The class above is an example of a simple Data Access Object (DAO). It allows us
to store and access messages from a record store. Now, we’d like to create a unit test
for the addMessage() method. It’s especially important to make sure that we always
close the record store after use. This should be done even if we get a RecordStore-
FullException. Let’s create test methods for both cases:

MessageDaoTest.java

Lets look at the setUp() method. First we use EasyMock to create a mocked
RecordStore object dynamically, that the MessageDao is going to write its data to.
Then we invoke RecordStore.setStaticInstance() to mock all static methods
in RecordStore. Static method invocations will be delegated to static_<method
name>()
equivalents.
Next is the test method testAddMessage(). The method starts preparing the en-
coded message and continues by setting up the expected behavior of the mock
object. You do this in EasyMock by invoking methods directly on the mock object.
In a sense, EasyMock ”records” these expected method invocations so it can com-
pare, or replay, the expectations with the actual invocations later, when the method
under test is called. If a method has a return value, you need to use a (statically
imported) expect() method. By using the andReturn() method, we can return a
mocked value.
Before executing the code we want to test, we invoke replay() to stop the mock
object from recording and set it to the replay state. All recorded method invocations
will be verifi ed from now on. After executing the code under test, we verify the
behavior by calling verify() on all mock objects. Finally, we use a standard JUnit
assert() method to assert the message ID. That’s it! The next test method uses
the same principle, except that we let EasyMock throw an exception during ad-
dRecord()
to test that we really close the record store even if we get an exception. A
test that could be quite diffi cult if running in a Java ME environment. This is one ex-
ample of how to use MockME and EasyMock for unit testing Java ME applications.
Once you get the hang of it, it’s easy to unit test your Java ME code in Java SE.

Eyes On The Prize

We’ve now seen how MockME has given us the opportunity to use Java SE tools,
such as EasyMock, for Java ME unit tests. But there is so much more, for example
code coverage. A code coverage tool is used to detect what parts of your code are
executed. This is a great tool for motivating developers to write test code. A code
coverage report indicates the percentage of your code that was tested (covered),
normally indicated with a green bar of variable length. It’s highly motivating, and
fun, to see how the green bar grows. Remember that it is not the number of test cas-
es that is important, but what parts of your code are executed by your test cases.
You are not limited to the above mentioned tools. You could use any Java SE unit
testing tool or testing library. For example, there are libraries that simplify testing of
multi-threaded classes, comparing XML documents, and so on. This would not be
possible, or at least it would be very hard, if you were to use one of the traditional
Java ME testing frameworks.

Conclusion

In this article we have shown how to write true unit tests for Java ME applications.
Running the tests in a Java SE environment gives us a lot of advantages and is pos-
sible thanks to MockME. So, should we throw away all our Mobile JUnit test code?
No. It’s important to distinguish between unit tests and integration unit tests. We
have stressed the use of true unit tests in Java ME, something rarely seen in Java
ME projects. There is always a need to test parts of your application on the target
platform, but in combination with true unit tests, the integration unit tests will be
fewer and more focused on integration issues.

Magnus Robertsson and Johan Karlsson

References

Dependency Injection
Mock Objects
MockME
JUnit
EasyMock
J2MEUnit
JMUnit
Mobile JUnit

Originally published in JayView.

This Post Has One Comment

  1. Interesting …Done a great job..

Leave a Reply

Close Menu