Coming Up for Air

Setting Up Droidium for Android Testing

Many know Arquillian as a great integration, functional, acceptance testing platform. Until recently, I thought of it solely as a great Java EE tool, but an Arquillian extension, known as Droidium, allows you to use Arquillian to help drive your Android testing. I spent some time tonight trying to get it set up for Cub Tracker and thought I’d share what (little) I have so far.

Rather than use Ant as the Android SDK currently does, we’re going to build this test app using Maven. My POM currently looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
            http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.steeplesoft.cubtracker</groupId>
        <artifactId>cubtracker-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cubtracker-arquillian-tests</artifactId>
    <description>Cub Tracker Arquillian Droidium</description>

    <!-- Properties -->
    <properties>
        <version.drone>1.2.0.Alpha2</version.drone>
        <version.arquillian.core>1.0.4.Final</version.arquillian.core>
        <version.droidium>0.0.1-SNAPSHOT</version.droidium>
        <android.avd.name>Nexus7</android.avd.name>

        <!-- maven-compiler-plugin -->
        <maven.compiler.target>1.6</maven.compiler.target>
        <maven.compiler.source>1.6</maven.compiler.source>
    </properties>

    <!-- Dependency Management -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.arquillian</groupId>
                <artifactId>arquillian-bom</artifactId>
                <version>${version.arquillian.core}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- Arquillian Drone BOM -->
            <dependency>
                <groupId>org.jboss.arquillian.extension</groupId>
                <artifactId>arquillian-drone-bom</artifactId>
                <version>${version.drone}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.jboss.arquillian.extension</groupId>
            <artifactId>arquillian-drone-webdriver-depchain</artifactId>
            <type>pom</type>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.arquillian.container</groupId>
            <artifactId>arquillian-droidium-container-depchain</artifactId>
            <version>${version.droidium}</version>
            <type>pom</type>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.arquillian.extension</groupId>
            <artifactId>arquillian-droidium-native-depchain</artifactId>
            <version>${version.droidium}</version>
            <type>pom</type>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>process-test-resources</phase>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
        </plugins>
        <!-- for filtering properties from this pom into arquillian.xml -->
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/arquillian.xml</include>
                </includes>
            </testResource>
        </testResources>
    </build>
</project>

There’s much of interest here, and, to be honest, I copied it mostly wholesale from here, which is pretty much the story for the other items we’ll look at. :P

Next up, arquillian.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.org/schema/arquillian
    http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <!-- Container configuration -->
    <group qualifier="containers" default="true">
        <container qualifier="android" default="true">
            <configuration>
                <property name="avdName">${android.avd.name}</property>
                <property name="droneHostPort">8080</property>
                <property name="droneGuestPort">8080</property>
            </configuration>
        </container>
    </group>

    <extension qualifier="droidium-native">
        <property name="serverApk">selendroid-server-0.4.2.apk</property>
    </extension>

    <extension qualifier="webdriver">
        <property name="browserCapabilities">android</property>
        <property name="remoteAddress">http://localhost:8080/wd/hub</property>
    </extension>
</arquillian>

This file is also copy and paste from the repo, with no changes made. A word of warning, though. If you look at the Droidium test project, you will see two .apks checked in: selendroid-server-0.4.2.apk, and selendroid-test-app-0.4.2.apk. The first is for the Selendroid project, which Droidium is built around. The second is the application we intend to test. I’m always bothered by libraries (jars, apks, etc) checked into source control, so I thought I’d be clever and have the build download that APK from Maven Central. Long story short, it doesn’t work as expected. The APK in Central and that found in the Droidium repo are not the same thing, so just paly along and check in this file (or script its download at build time :).

Next up, the test case:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RunWith(Arquillian.class)
@RunAsClient
public class CubTrackerTest {
    @Deployment(name = "android")
    @TargetsContainer("android")
    public static Archive<?> createDeployment() {
        File archiveFile = new File("../app/bin/cubtracker-debug.apk");
        return ShrinkWrap.createFromZipFile(JavaArchive.class, archiveFile);
    }

    @Test
    @OperateOnDeployment("android")
    public void dumbTest(@ArquillianResource AndroidDevice android,
            @Drone WebDriver driver) {
        driver.findElement(By.id("menu_add_scout")).click();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
            Logger.getLogger(CubTrackerTest.class.getName())
                .log(Level.SEVERE, null, ex);
        }
    }
}

The @Deployment method is pretty simple; we just point to the APK of the app to test. Whereas the Droidium test checked that into source control, in my context, the file is built as part of the larger process, so I provide a relative path to the APK. Finally, in my test, all I have it doing here is clicking on the "Add Scout" menu. Interestingly, rather than specify the text of the menu, I look up the widget at runtime via its ID. As you should know, though, Android IDs are numeric, but I’m passing a string, so it seems that Selendroid is smart enough to take "menu_add_scout" and find R.id.menu_add_scout (which is probably just simple reflection, but still. That’s pretty cool. :). The Add Scout activity should show, the test waits 5 seconds so I can see that it actually did something, and then everything shuts down: the test, the emulator. Everything. I can manually start and stop the emulator if I want, in which case Droidium doesn’t shut it down, but, just like Arquillian can start and stop your app server instance for you, it can do the same for your Android emulator. And that’s pretty cool too.

This just scratches the surface, of course, as now Selendroid needs to be explored and understood, but that’s a different topic. Hopefully, what I’ve presented here will be enough to get you going with Droidium so you can quit worrying about emulator management and focus on writing tests, which is what Arquillian is all about. :)

Quotes

Sample quote

Quote source