Skip to end of metadata
Go to start of metadata

There are several topics we wanted to dig in:

Modularity

Modularity problem with Activities

While building an Android application, you have to define Activities that represent the screens of your application.
These Activities contains UI code, but also resources for graphical features:

  • screen layout definition
  • labels
  • icons

Because it's hard to define an Activity without having dependencies, it's hard to reuse activities between projects.

ADT builder (the Eclipse plugin) generates a Java class R.class that defines all the resources constants, and the Activity class is built (linked) against this R.class.
This system is good because all resources dependencies are resolved at build time so it avoids mistakes and also allows the IDE to support auto-completion and refactoring for the resources identifiers.

But if you want to define an Activity in a project and reuse it in another one, then the resources resolution system becomes a problem.
If you try to simply import an activity from one project to an other, the activity can not be built anymore, because its resources are missing.

Modularity use cases

Inside nuxeo-android-connector we want to provide all the services needed to communicate with Nuxeo server, but ideally, we also want to provide reusable screens (Activities).
There are typically some screens that you don't want to rewrite in each application you build:

  • Network and Cache configuration screen
  • Login / Password screen
  • Simple document listing screen
  • Document View/Edit screen

Testing RIndirect

The project RIndirect provides a way to automatically translate the Resources to allow resolution of resources for the imported Activity.
Rindirect requires to ask maven-android-plugin to generate the apk source (attachSources=true), then the module is usable in Maven as a dependency of type apksources.
We managed to have it working using Maven build, but we did not find a way to make it work in a stable way using Eclipse ADT.
This means that it worked from the command line, but not from Eclipse.

APKlib

We found out that RIndirect was kind of deprecated method since there was now some native support for "resources translation" inside Android SDK.
The idea is that in addition of standard jars, you Android project (APK) can also depend on APKlib modules.
APKlib packaging model allows for a native support of Resources translation.

This worked very quickly in the maven build : just externalize reusable Activity classes in a separated maven module, choose apklib packaging, and make this new module a dependency of your application module.

On the Eclipse ADT plugin side, it was less easy. Depending of the exact version of ADT, the configuration and the build behavior are different: APKlib management in ADT is still a work-in-progress.
There are issues with the Eclipse build path when the APKlib project depends itself on other projects in Eclipse. Dalvik/ProGuard will complain about class duplication on packaging/dexing time.
Also, APKlib implementation in Eclipse has changed a lot in the past weeks and may change again. ADT upgrade fails to properly update Eclipse projects, they must be manually fixed.
Finally, ADT plugin makes use of new options in the Android SDK which must be updated accordingly.
Note Android SDK Tools revisions 12 and 13 had blocker issues fixed since revision 14.

That was not easy but we succeeded in getting a working status in both Eclipse and Maven (command line).

Test system

Android SDK being based on Java and Eclipse, it's easy to get started.
But when you are used to TDD, you really miss the testability.

The point is that since Android code runs in a Dalvik VM you need to run an emulator to be able to run your code and test it.
The default testing framework provided by Android allows to instrument the Activities to test their lifecycle and UI interactions.
This framework is more suited for doing Non regression testing than pure unit testing.

Non Regression testing

Building the test

We started with the simplest solution : test the Android Application through its UI.
This means that for testing nuxeo-android-connector we will actually test nuxeo-android-automation-samples that is a sample application using the connector.

For that we had to create a dedicated testing project containing the Test activities which instrument the activities from the sample.
The testing scenario is to use the Browse repository features in several condition :

  • browse + create document in online mode
  • browse + edit document in online mode
  • browse + create document in offline mode, go back online and verify synchronization
  • browse + edit document in offline mode, go back online and verify synchronization

That leads to good, although simple, non regression testing of the DocumentLists and offline management features.

For building the test, we used Robotium that provides a higher level API.

We ended up having the same types of issues we already had with other UI testing tools such as WebDriver and Selenium :

  • need to have a wait to define precisely when a screen is loaded and ready to run
  • need to use timers + retry loops to manage async operations

So, it's easy to have a test running once, but building a test that will always pass independently of the speed of the application requires more work.

Although the Maven integration worked well, we experienced some limitations when running tests from ADT.
Typically, the testing application can not have any reference to a library that would be used by the application you are testing.

In our context, it means that nuxeo-automation-browse-test (the testing application) will depend on nuxeo-automation-sample (the application we are testing), but can not depend on nuxeo-android-connector.
This is a big limitation because from inside the test, we need to monitor if the data is loaded and we also need to access some attributes of the Documents displayed in the screens, but we can not!
This really looks like a bug or limitation in ADT which will hopefully be solved soon in an update.
Until that, we added some testing dedicated methods in the sample application so that the test application get access to all required features and data without having to depend on the connector. For the same reason, we also used some Java reflection in the tests...

Running the test under CI

Running the tests via Maven was easy.
The hard part was to automate all the testing requirements:

  • start an Android Emulator
  • deploy and start a Nuxeo server
  • build and deploy android applications (sample application + testing application)
  • run the Robotium tests

We're using Jenkins for our continuous integration.

Since we could automate the build and tests with Maven (thanks to the Maven Android plugin), it was easy to integrate into Jenkins.

The Android Emulator Plugin provides useful features for managing the Android emulator, like ADT does within Eclipse. So, we configured Maven to not manage the emulator when it's already started by Jenkins.

As we already did for other Nuxeo functional tests, we're using nuxeo-distribution-tools for managing the Nuxeo server (download, start, stop).

Sadly, the tests results were not properly integrated by Jenkins, since they are run from the maven-android-plugin instead of maven-surefire-plugin, even if they are using JUnit.
UPDATE: that issue (JENKINS-10913) is fixed since Jenkins 1.434 and maven-android-plugin 3.0.0-alpha-6 but it requires Maven 3.

Unit testing low level features

So far, it seems that Android SDK only allows to unit test an activity.
This makes sense for testing an application, but for low level features of a framework (storage, caching, events ...) this is really not easy.

May be we did not search enough, but we did not find an alternative as long as you run the tests on the Dalvik VM.
The only solution we found to be able to perform real unit tests is to run in pure Java without any dependency on the Android SDK.
The key point is that you can not compile your test against the Android SDK, otherwise it will fail with a "stub exception" because some of the class of the SDK are actually not implemented in Java.

This solution is not very complicated but implies that you need to abstract or mock any dependency you may have on the Android API.
In our case, this includes :

  • Storage API
  • Event system
  • Uri

Anyway since this seems to be the only available solution for having real testability, we will do this work ...

The com.google.android:android-test Maven artifact provides a few testing features and will probably grow in the future.

Eclipse and Maven project standardization

For now, it was not possible to easily generate the Eclipse project from the Maven one (using maven-eclipse-plugin). We did not try to fix that part since it was undergoing a lot of changes from a SDK/ADT revision to another.

When the Android Eclipse project specifications will be standardized (see above remarks about APKlib for instance), we will try to provide a generic solution for importing Maven Android projects into Eclipse. Currently, our workaround is to commit inside the project some .classpath.ok and .project.ok files the developer can copy and respectively rename to .classpath and .project.

Another difference between Maven and Eclipse build is the certificate used for signing the applications.
ADT currently allows to configure it only at the Eclipse user workspace level. That solution is not compliant with having the keystore used by Maven being included in the project sources.
This is a pain if you switch often from Maven to Eclipse as it requires to uninstall an APK signed with a given certificate before being able to upgrade it with an APK signed with another certificate.

Labels