-
Notifications
You must be signed in to change notification settings - Fork 0
Android and tests, what happens with project layouts ?
The new Android build system based on Gradle is quite promising. It is still in beta but already allows for different flavors, launching tests with reporting, getting emma support. It is planned to allow to launch lint and more alternative technologies like robolectric or uiautomator soon.
But all this will have a deep impact on layouts of Android projects layouts : either we have to separate projects our main app and libraries and their tests, or we can group apps and tests together, libs and tests, even grouping that all in a project with sub-projects.
In this article, we will demonstrate that three problems are tightly knit :
- The layout of android projects, and this question is increasingly pregnant with respect to the emergence of new testing technologies
- The scope of dependencies inside projects and tests
- The distribution of the jars of Android and extra libraries
The old layout of Android projects is to create separate projects for every thing :
- a project for each app
- a project for each lib
- a project for each set of tests for apps or libs
The actual layout recommended on the new build system of Android groups apps and tests together and this sounds good, simpler :
- src/main/java : main app or lib under tests
- src/instrumentedTests/java
But does it mean we will soon be able to have other folders like :
- src/robolectricTests/java
- src/uiAutomatorTests/java
- src/calabashTests/java ?
- src/spoonTests/java ?
The recommended layout (merging app and instrumented tests) works pretty well already with Gradle, and it should be relatively easy to extend the Android Gradle DSL to get robolectric (or uiAutomator tests) grouped in the same project as well (alternative link).
But if it works thourgh Gradle command lines, it completely fails in Eclipse ADT as there is no way to distinguish the scope of libraries. If you need a lib in your tests only, it will be added to the whole project and bundled into your apk.
The new Android Private Librairies container of ADT offers a workaround. It allows to use some libs for testing/compiling and prevents them to be exported, they will not be included to your final apk or aar.
But this solution can't solve the problem of adding more test technologies in the same project. If layout of projects allows to group all tests, Android ADT should move to a more dynamic and scalable way of defining library containers and use them to scope dependencies.
Actually, having a single "private container"solves, indirectly, an aspect of the problem of the distribution of android artefacts. As there is no central repository, to get SDK jars or extras, and there will never be one , an increasing number of Android libraries use their own versions of the support library. To avoid those different versions of the same lib to conflict when dexing, the android private libraries container can be filled with unwanted versions of the libraries and let a dev define its own and place it in the usual Android containers. When the final apk will be built, it will contain only the desired very own version of all libraries, but for tests, all containers are taken into accounts.
This, coupled with the new "maven repository" that is available through the SDK download manager offers a good enough alternative to the problem of versioning of Android extra libraries. The new repository will let you build your projects with the newest version of Android extra libraries, available locally on your computer, in a standard way. If this system becomes more scriptable, it could even be used for continuous integration soon.
The private library container is already useful : it completely solves the problem of versioning of libraries. This will participate in solving the problem of the distribution of the jars of Android SDK and extras. But, it can't solve all of dependency management, especially giving dependencies a proper scope. If the actual direction is followed, there could be more scope containers in ADT to properly solve it.
Actually, this solution would fit pretty well with gradle and an extension of the actual DSL, and it is tempting, by its elegance. It would be possible to add new extensions to handle robolectric, uiautomator and so on. It could like :
dependencies {
//for dependencies found in artifact repositories you can use
//the string notation, e.g. group:name:version
compile 'commons-lang:commons-lang:2.6'
testCompile 'org.mockito:mockito:1.9.0-rc1'
robolectricCompile 'org.robolectric:robolectric:2.1'
uiAutomatorCompile 'com.google.android:uiautomator:18'
//map notation:
compile group: 'com.google.code.guice', name: 'guice', version: '1.0'
}
the different set of dependencies could be bound to ADT containers via an extension of the Eclipse plugin.
It would be much harder for maven to cope with it as maven only offers a single "test" scope, all set of tests would have to share the same libraries.A different approach with Maven would be to use profiles to separate the different dependencies and give them a separate scope. This approach though is already difficult to setup to distinguish unit and integration tests (alternative link). And, moreover, the actual state of maven integration in Eclipse doesn't offer a very good perspective on this. Switching from a profile to another is really a pain in Eclipse.
Another solution could be to bind profiles to library containers in Eclipse via m2e-android. But that would be some hack of maven profiles, they have not been designed for this. Maven profiles are closer to Android Gradle build types than dependency sets and they are already used this way (for instance to sign a release apk).
In either case, using Gradle and or Maven, if you use Eclipse and ADT, you can't properly move to the new layout of Android projects recommended by Gradle. It is actually not possible to solve properly the problem of the scope of libraries. If it looks feasible via Gradle, it will take much more effort on Eclipse to fully support multiple set of tests grouped in a same project with an app or a lib.
Having a different project for each app, each lib, and each set of tests is the only actual solution that works and scales up. It also ends up to be quite clear compared to having a bunch of source folders and resources folders tied to a given run configuration. Every set of tests, or app, or lib fits pretty well inside a full project of its own.
This solution works pretty well in all IDEs, both Eclipse and IntelliJ, and can be very well accomodated by Gradle (via Multi-Projects builds), and already works pretty well with maven android plugin and m2e in the current state of the art.
Wouldn't it be better to keep the old layout as the future of Android project layout and extend it by simply creating new subprojects for every testing technology ? Wouldn't it be better to encourage this layout and have a Gradle plugin for 'android-instrumented-tests', 'android-robolectric-tests', 'android-uiautomator-tests', etc.