Genystory | Harald Kahlfeld & Thomas Rebouillon (Mobile.de)
Mobile.de’s Android app is continuously improved with Genymotion Gradle Plugin. Mobile.de’s QA Engineer & Senior Android Developer share their experience.
Mobile.de’s Android app is continuously improved with Genymotion Gradle Plugin. Mobile.de’s QA Engineer & Senior Android Developer share their experience.
What is the main goal of Continuous Integration? CI (Continuous Integration) prevents integration problems by pushing smaller code changes more often. CI is meant to be used along with automated unit tests. That’s where Genymotion comes into play One of Genymotion’s key features is its Gradle Plugin which allows you to combine the Gradle build system with Genymotion. This allows you to declare the virtual devices you want to start prior to testing. How does it work? To use Genymotion on a continuous integration server, you automate the launch via the Gradle plugin. This tool will simply create, configure and launch devices automatically, before your tests run. Unit tests are run on an integration server like Jenkins which connects to Genymotion, installs the apps inside the virtual devices and launches tests using Genymotion Virtual Machines. For example, one client, has a server instance, running specific tests with sensors, telephony and SMS/GPS with an undetermined number of Virtual Machines. Their need to have multiple and varied custom configurations for testing on demand is met by Genymotion. Bottom line Our Android Emulator Genymotion can be used to integrate elements of your testing scheme and validate your code. You can reset and reuse virtual machines easily and rapidly. With components such as Gradle, Android Studio & Eclipse plugins, command line tools and a Java API, Genymotion stands out as a powerful and robust solution for testing and integration. Watch our webinar and get more practical tips
How to manage your build configuration, from the developer platform to the CI Before starting this article, I want to thank Mark Vieira, Gradle core developer, and the publishing teams from Gradle and Genymobile for their review and assistance. TL;DR The syntax apply from: allows you to inject Gradle scripts from files. You can check the current defined environment variables to know the current build environment (developer platform or continuous integration server) and apply the corresponding files. All parameters you don’t want to write in clear can be defined outside your Gradle files using Gradle Properties (via gradle.properties files, environment variables or command line arguments) and used as variables into Gradle files. Some of these methods can be controlled directly from the continuous integration management interface. Introduction Two years ago, at Genymobile, we were all very enthusiastic about one of Google I/O announcements: Gradle is going to be the future-new-official-but-still-young build system for Android. A few weeks later, when we measured its potential power, all Android app developers were waiting impatiently for a stable and fully usable duet “Android Studio & Gradle”. Our first observation after adopting Gradle was that we were living a real professionalization of the tools provided by Google. In short, Gradle was as powerful as the well-known Maven, with a very light boilerplate for the configuration and the customization of the build. It also had this very nice wrapper that really made the adoption enjoyable. Gradle led to a better industrialization of the Android development, with an official support by the Android SDK team. Now we all implement automated tests, don’t we? We take seriously into account static analysis tools, right? And we run all of them on our continuous integration server, sure! Thanks to the build.gradle files of our projects, we can now control all tasks we run automatically. But sometimes, some tasks must behave differently whether they are run on our development environment or on a continuous integration server (also named CI). An example of the problem At Genymobile, we develop Genymotion, an Android emulator. We worked hard to make it the perfect emulator for Android developers and we recently released a Gradle plugin. This plugin allows us to declare, in the build.gradle script, the Genymotion devices to launch before running the tests. Let’s imagine that on our computer, we want to run tests on an emulated phone (Nexus 5) and an emulated tablet (Nexus 9). Then we add this block to the build.gradle file: genymotion { devices { Nexus5 { template “Google Nexus 5 – 5.1.0 – API 22 – 1080×1920” } Nexus9 { template “Google Nexus 9 – 5.1.0 – API 22 – 2048×1536” } } } On the CI server side, we want to test more devices. So we add a Galaxy S3, an S4, a Nexus 7 and a Nexus 10, with different Android versions: genymotion { devices { Nexus5 { template “Google Nexus 5 – 5.1.0 – API 22 – 1080×1920” } Nexus9 { template “Google Nexus 9 – 5.1.0 – API 22 – 2048×1536” } S4 { template “Samsung Galaxy S4 – 4.3 – API 18 – 1080×1920” } S3 { template “Samsung Galaxy S3 – 4.2.2 – API 17 – 720×1280” } Nexus7 { template “Google Nexus 7 – 4.1.1 – API 16 – 800×1280” } Nexus10 { template “Google Nexus 10 – 4.4.4 – API 19 – 2560×1600” } } } Those additional configurations offer a better coverage for the instrumented tests and they will all be run automatically. Now, we have two pieces of script, supposed to be in the same file at the same time. Let’s see how we can handle this problem. Apply From a File The best way to solve this is to put both scripts into separate files: genymotion-dev.gradle will be applied when the build will be running on the developer’s computer; genymotion-ci.gradle will run on the CI. They can be both be applied using the apply from: syntax, as shown below: build.gradle apply from: “$rootDir/genymotion-dev.gradle” But how can the build system decide what files to apply? For this, it needs to know whether the build is running on the CI or on a dev environment, and it is pretty simple. Each time your continuous integration server launches a job, it opens a worker and it injects some environment variables. These can be used by the running scripts to get information about the context of the build. They are also convenient to know whether the Gradle script is executed on a CI or not. You can get the list of these default values on each CI documentation. Here are examples for Jenkins and Teamcity. Some people would prefer to avoid the build system configuration to depend on the CI installed, allowing to switch easily from a server to another. To solve this, you can define an environment variable yourself for all your jobs instead of using the one from your server. On Jenkins, you can do this using EnvInject plugin. On TeamCity, you can use Build Parameters. We just need to test that this environment variable is set to determine whether the build is running on the CI and then apply the dedicated Gradle file: build.gradle def isCiServer = if(System.getenv().containsKey(“IS_CI_JOB”)) if(isCiServer) apply from: “$rootDir/genymotion-ci.gradle” else apply from: “$rootDir/genymotion-dev.gradle” The path as a URL As a bonus, Gradle adds a nice feature to the apply from instruction: we can use a URL. Instead of storing the file in a specific place on the CI server or on the repository, we can put it behind a HTTP server like this: apply from: “http://ci.mycompagny/genymotion-ci.gradle” Managing Local Parameters Now that the build setup is defined, we are almost done for the build system configuration. However, in most cases, you still need to configure settings related to the computer running the build. These settings can be related to a specific local path, credentials, or any other specificity of the