Posts mit dem Label android werden angezeigt. Alle Posts anzeigen
Posts mit dem Label android werden angezeigt. Alle Posts anzeigen

Dienstag, 25. Februar 2014

Attaching debugger when running Robolectric tests from Android Studio

The Gradle View (View → Tool Windows → Gradle) in Android Studio allows you to easily run your unit tests from within Android Studio. From the tree of tasks for your project, just select the task test and run it.
It's also possible to attach the debugger to that process executing your Robolectric tests by simply choosing Debug instead of Run. However, you might face this error:
Error running android-app:appname [test]: Unable to open debugger port : java.net.SocketException "Socket closed" 
In order for the debugger to attach you need to uncheck "Use in-process build" in the settings menu for Android Studio under Compiler → Gradle.

Freitag, 17. Januar 2014

Getting started with Robolectric and Gradle in Android Studio

Unit tests are an invaluable tool to ensure software behaves the way it is supposed to which you want to check as often as possible while developing. However, running tests on an Android emulator is slow. Robolectric is a unit test framework that enables you to run your Android unit tests as plain vanilla JUnit tests inside the JVM on your computer, which is much faster and less cumbersome than to run them inside the emulator.

New Android projects are built with Gradle

There is a plugin for Gradle called gradle-android-test-plugin that will run unit tests relying on Robolectric and compile the results in a test report. The following will describe how to change your build.gradle to set this up. First, pitfalls along the way will be shown, followed by a working build.gradle configuration. Finally, a few tweaks have to be made to integrate this properly with Android Studio.

Pitfalls and how to work around them

These are problems I encountered when trying to set up the gradle-android-test-plugin and how to circumvent them.

Gradle cannot determine dependencies

* What went wrong:
Could not determine the dependencies of task ':MyApp:testDefaultFlavorDebug'.
> A base directory must be specified in the task or via a method argument
Solution: Use 0.9.1-SNAPSHOT version of the gradle-android-test-plugin., it fixes this problem.

Robolectric: Resource path restrictions

java.lang.IllegalArgumentException: Resource path must end in "/res"
    at org.robolectric.res.PackageResourceLoader.<init>(PackageResourceLoader.java:20)
    at org.robolectric.res.PackageResourceLoader.<init>(PackageResourceLoader.java:11)
Solution: Use 2.3-SNAPSHOT version of Robolectric, it fixes this restriction on the path name.

Robolectric: No KitKat

java.lang.UnsupportedOperationException: Robolectric does not support API level 19, sorry!
Solution: As defined per SdkConfig.java, Roboelectric only supports SDKs 16 through 18. Change targetSdkVersion to 16, 17 or 18.

Summary of necessary changes to build.gradle

Your build.gradle has to be changed in quite a few places to set up gradle-android-test-plugin. Here's the condensed summary of theses changes:
buildscript {
    repositories {
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
    }
    dependencies {
        classpath 'com.squareup.gradle:gradle-android-test-plugin:0.9.1-SNAPSHOT'
    }
}
apply plugin: 'android-test'
repositories {
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
dependencies {
    testCompile 'junit:junit:4.10'
    testCompile 'org.robolectric:robolectric:2.3-SNAPSHOT'
    testCompile 'com.squareup:fest-android:1.0.+'
}

Write a first unit test

Now you can create a unit test. Create a file in src/test/com.myapp.MyTest.java:
package com.myapp;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

import static org.junit.Assert.assertTrue;

@RunWith(RobolectricTestRunner.class)
public class MyTest {
    @Test
    public void testNumberOne() {
        assertTrue(false);
    }
}
Note that with Robolectric 2.3-SNAPSHOT you do not need RobolectricGradleTestRunner anymore, since it is integrated. When you now run ./gradlew test in your project, it should run that very unit test, which will -- by design -- fail:
com.myapp.MyTest > testNumberOne FAILED
    java.lang.AssertionError at MyTest.java:13

Integrating with Android Studio

So far, we have a working set up of Gradle, Robolectric and the gradle-android-test-plugin -- but only for the console. When we try to "Sync Project with Gradle Files" in Android Studio, the IDE will not pick up our newly created src/test folder and it will not provide content assist when writing code.
This is because Android Studio expects test code to be in src/instrumentationTest, but luckily we can change that in build.gradle:
android {
    sourceSets.instrumentTest.setRoot('src/test')
}
Android Studio also cannot compile the test code because it misses dependencies. This is because it expects these dependencies to be defined in the configuration named instrumentTestCompile, whereas currently, we are using the testCompile configuration only. We have to add the dependencies defined earlier to the configuration Android Studio uses. This can even be achieved without repeating them, but by creating a copy, like so:
dependencies {
    instrumentTestCompile configurations.testCompile.dependencies
}

Summary

This article described how to overcome the problems when adding the gradle-android-test-plugin to a project. It provided a configuration for Gradle to run a sample unit test leveraging the Roboelectric library. In the last step, it showed how to alter the configuration so that Android Studio would play along.

Dienstag, 3. Dezember 2013

Fix for Genymotion: "Unable to load VirtualBox engine"

Recently I tried out Genymotion, a new emulator for Android that is based on virtual machines inside of VirtualBox. Genymotion easily beats the standard Android emulator hands down: it boots much faster and runs super slick and smooth.
It was very easy to set up and create devices, too. However, after the next reboot of OSX Mavericks, I couldn't start the machines anymore and instead got this:
Unable to load VirtualBox engine.
Make sure that you have installed it correctly before starting Genymotion.
For more information refer to: https://cloud.genymotion.com/page/faq/#vbox

It turns out that this is due to missing kernel extensions that VirtualBox requires to operate which are not automatically loaded. The following command will load them:
sudo /Library/StartupItems/VirtualBox/VirtualBox restart
After running the command Genymotion will be able to start devices again. However, this is will only solve the problem once, I am still looking for a permanent fix.