Sunday, January 17, 2010

Android Continuos Integration: Build with maven

Android Continuous Integration: Build with maven









This document can be read in Google Docs (http://docs.google.com/View?id=ddwc44gs_226ds4kfpff), cut and paste link if you have problems accessing it.

If you have problems seeing the images, like the icon in this box don't blame me, it seems to be an issue when you publish to Blogspot from Google Docs.



















introduction




"Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible."










We have written some tests for our project and now we would like to take continuous integration into account, mainly if we are working in an enterprise development environment and our team has several developers.



We all are very much accustom to use Eclipse and Android ADT as our main developer environment, so it's logical that even though we are increasing our team size we don't want to left behind that experience and start using a different tool from scratch. At the same time, it's pretty clear that using maven as a build tool for the project we obtain a great level of simplification, mainly if we use hudson, cruise control or similar continuous integration tools.



That's why the approach presented here is to let both models co-exist.










    project structure




    We are also considering here that a VCS is used and a parent project containing both the main and test project is under this VCS control.



    The structure of our project P1 will be
















    the P1 project including AP1 application's main project and its tests in AP1Test.

    To create these projects you should follow this sequence:






    1. Create a Project (File -> New... -> Project), P1 in this case
    2. Add a pom.xml file (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
    3. Create an Android Project (New... -> Android Project), AP1, using P1 folder instead of the default location, but DON'T create the test project yet or it will fail (ADT 0.9.5)
    4. Add a pom.xml file to AP1 (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
    5. Selecting AP1 project, create the corresponding test project AP1Test (Android Tools -> New Test Project) using again P1 as the location
    6. Add a pom.xml file to AP1 (File -> New... -> Other -> Maven -> Maven POM file). We will review its content later
    7. You can now import the whole project structure to your VCS





    pom files


    You need to define some things inside your pom files in order to get the Android projects correctly built.

    P1's pom.xml


    We are using maven-android-plugin here, visit its site for further details.







     1 <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">


     2   <modelVersion>4.0.0</modelVersion>


     3   <groupId>P1</groupId>


     4   <artifactId>P1</artifactId>


     5   <packaging>pom</packaging>


     6   <version>0.0.1-SNAPSHOT</version>


     7  


     8    <build>


     9         <sourceDirectory>src</sourceDirectory>


    10         <plugins>


    11             <plugin>


    12                 <groupId>org.apache.maven.plugins</groupId>


    13                 <artifactId>maven-compiler-plugin</artifactId>


    14                 <version>2.1</version>


    15                 <configuration>


    16                     <source>1.6</source>


    17                     <target>1.6</target>


    18                 </configuration>


    19             </plugin>


    20             <!--


    21                maven-android-plugin doesn't delete files from bin,


    22                only from target, we are deleting them here


    23             -->


    24             <plugin>


    25                <groupId>org.apache.maven.plugins</groupId>


    26                <artifactId>maven-clean-plugin</artifactId>


    27                <version>2.2</version>


    28                <configuration>


    29                   <filesets>


    30                      <fileset>


    31                         <directory>bin</directory>


    32                         <includes>


    33                            <include>**/*</include>


    34                         </includes>


    35                      </fileset>


    36                      <fileset>


    37                         <directory>gen</directory>


    38                         <includes>


    39                            <include>**/*</include>


    40                         </includes>


    41                      </fileset>


    42                   </filesets>


    43                </configuration>


    44             </plugin>


    45             <plugin>


    46                 <groupId>com.jayway.maven.plugins.android.generation2</groupId>


    47                 <artifactId>maven-android-plugin</artifactId>


    48                 <configuration>


    49                     <sdk>


    50                         <!--

                                <path>${env.ANDROID_HOME}</path>

                               --
    >


    51                         <path>/opt/android-sdk/</path>


    52                         <platform>2.1</platform>


    53                     </sdk>


    54                     <deleteConflictingFiles>true</deleteConflictingFiles>


    55                 </configuration>


    56                 <extensions>true</extensions>


    57             </plugin>


    58         </plugins>


    59     </build>


    60    <modules>


    61       <module>AP1</module>


    62       <module>AP1Test</module>


    63    </modules>


    64    <dependencies>


    65       <dependency>


    66          <groupId>android</groupId>


    67          <artifactId>android</artifactId>


    68          <version>2.1</version>


    69          <type>jar</type>


    70          <scope>provided</scope>


    71       </dependency>


    72    </dependencies>


    73


    74     <dependencyManagement>


    75         <dependencies>


    76             <dependency>


    77                 <groupId>android</groupId>


    78                 <artifactId>android</artifactId>


    79                 <version>2.1</version>


    80                 <scope>provided</scope>


    81             </dependency>


    82         </dependencies>


    83     </dependencyManagement>


    84 </project>





    Basically we
    1. define our source directory as src to be compatible with the Eclipse Android project structure
    2. configure the maven-compiler-plugin according to our needs
    3. clean Eclipse Android project structure too in our clean goal
    4. configure maven-android-plugin defining the location of the Android SDK. This can be defined here or read from the environment
    5. define our modules, in this case the main project AP1 and its tests AP1Test
    6. define the dependencies, android 2.1 in this case

    AP1's pom.xml





     1 <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">


     2   <modelVersion>4.0.0</modelVersion>


     3   <groupId>AP1</groupId>


     4   <artifactId>AP1</artifactId>


     5   <packaging>apk</packaging>


     6   <version>0.0.1-SNAPSHOT</version>


     7   <parent>


     8    <artifactId>P1</artifactId>


     9    <groupId>P1</groupId>


    10    <version>0.0.1-SNAPSHOT</version>


    11   </parent>


    12 </project>










    AP1Test's pom.xml





     1 <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">

     2   <modelVersion>4.0.0</modelVersion>

     3   <groupId>AP1Test</groupId>

     4   <artifactId>AP1Test</artifactId>

     5   <packaging>apk</packaging>

     6   <version>0.0.1-SNAPSHOT</version>

     7   <parent>

     8    <artifactId>P1</artifactId>

     9    <groupId>P1</groupId>

    10    <version>0.0.1-SNAPSHOT</version>

    11   </parent>

    12   <dependencies>

    13    <dependency>

    14       <groupId>AP1</groupId>

    15       <artifactId>AP1</artifactId>

    16       <version>0.0.1-SANPSHOT</version>

    17    </dependency>

    18    <dependency>

    19       <groupId>AP1</groupId>

    20       <artifactId>AP1</artifactId>

    21       <version>0.0.1-SNAPSHOT</version>

    22       <type>apk</type>

    23    </dependency>

    24   </dependencies>

    25 </project>







    populating your local repository


    For maven to find android dependencies you must populate your local repository. To ease the task you can use android-mvn-install script which do all the hard work for you.
    Just run

    $ wget -qO - http://android.codtech.com/android-tools/android-mvn-install | bash -s -- --sdk-dir=/opt/android


    or download from its page and run it locally.
    There's an alternative for this step, maven-android-sdk-deployer, however I prefer the simplicity and flexibility of the android-mvn-install script.

    building the project

    Now we have several alternatives to build our project:
    • using Eclipse as usual for other Android projects using ADT plugin (i.e.: AP1 -> Run As -> Android Application)
    • using Maven from Eclipse (i.e.: P1 -> Run As -> Maven build)
    • using Maven from the command line (i.e.: mvn install)
    • using a continuous integration tool

    using hudson

    Having followed all the steps mentioned before we will now be able to create a job in hudson to build our project using maven











    conclusion

    There are still some rough edges and some things we haven't mentioned yet like running headless android emulators to be able to run the tests on the server. We will be covering these issues in future posts but I think that we have enough already to start applying continuous integration for our android projects.


    Comments are gladly welcome.






    Copyright © 2010 Diego Torres Milano. All rights reserved.

    Tuesday, December 22, 2009

    Android Testing: ContentProvider integration tests using mock con

    Android Testing: ContentProvider integration test using mock content



















    This document can be read in Google Docs (http://docs.google.com/View?id=ddwc44gs_221tm2kqmgp), cut and paste link if you have problems accessing it.












    But when we try to produce functional or integration tests of your Activities against your ContentProvider it's not so evident what test case to use. The most obvious choice is  ActivityInstrumentationTestCase2 mainly if your functional tests simulate user experience because you may need sendKeys() or similar methods, which are readily available on these tests.





    The first problem you may encounter is that's no clear where to inject a MockContentResolver in your test to be able to use a test database instance or database fixture with your ContentProvider. There's no way to inject a MockContext either.


    We have visited Mock Objects before and evaluated Test Driven Development. You may want to take a look if you are not familiar with mock objects on Android.

    An alternative approach to this problem is introduced by tenacious33 in Android functional testing with MockContext.







    mocking content provider operations


    In order to create an ActivityInstrumentationTestCase2 and be able to mock content provider operations and optionally use a database fixture we need a different approach.



















    Download:

    Binaries and source code of
    android-mock and MockContentResolverActivity main and test sample projects can be downloaded from http://sites.codtech.com/android/Home/source-code




    android-mock



    android-mock is a library encapsulating some of the needed functionality to simplify functional and integration tests of activities that use content providers.

    We are introducing the library here and we will be presenting a practical example later.
    This is the UML Class Diagram of the library. Green classes belong to android library.













    ActivityAndContentProviderInstrumentationTestCase2<T extends Activity, P extends ContentProvider>


    This is a functional or integration test of an Activity that uses a MockContextWithMockContentResolver context and uses the specified ContentProvider under tests.

    You should extend this class to create your tests and everything will be setup automatically.

    During the setup process the abstract method createDatabaseFixture(Context mockContext) is invoked to give you the opportunity of creating your database fixture.

    The last step of the setup process invokes your Activity's onCreate() method.

    MockContextWithMockContentResolver<P extends ContentProvider>


    A mock context, with a mock content resolver using the specified ConentProvider under test. The mock context is a RenamingDelegatingContext that renames file and database operations prefixing them with "test." by default.

    This provides a way to inject a ContentResolver into an ActivityAndContentProviderInstrumentationTestCase2 permitting the introduction of database fixtures.

    GenericMockContentResolver<P extends ConentProvider>


    A generic mock content resolver using the specified ConentProvider under test. This content resolver uses the specified content provider to resolve data for authority.



    examples


    To demonstrate the use of this library we have an Activity that presents some data using a ContentProvider. They are very simple an similar to other examples with the same functionality. In this case we are using a content provider with authority on a simple table Samples.AUTHORITY.
    Our Activity in the following example is MyMockContentResolverActivity and the content provider is MyContentProvider.








    We will concentrate now on the tests.

    content provider unit tests


    This tests will be as usual implemented using ProviderTestCase2<P>. This is pretty standard so we are not pay much attention to it.



    activity and content provider functional tests



    We reach the point. MyMockContentResolverActivityTests class implements the tests extending ActivityAndContentProviderInstrumentationTestCase2 and specifying the Activity and ContentProvider under test as well as the authority, Samples.AUTHORITY in this particular example.
    These tests can be run completely isolated from real data, so you can be confident and exercise your database through your Activity not worrying about your real data.















    /*
     * Copyright © 2009 COD Technologies Ltd.  www.codtech.com
     * 
     * $Id$
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.codtech.android.training.mockcontentresolver.examples.test;

    import android.app.Activity;
    import android.content.ContentResolver;
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.net.Uri;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.widget.TextView;

    import com.codtech.android.mock.ActivityAndContentProviderInstrumentationTestCase2;
    import com.codtech.android.training.mockcontentresolver.examples.MyMockContentResolverActivity;
    import com.codtech.android.training.mockcontentresolver.examples.provider.MyContentProvider;
    import com.codtech.android.training.mockcontentresolver.examples.provider.Samples;

    public class MyMockContentResolverActivityTests extends
    ActivityAndContentProviderInstrumentationTestCase2<MyMockContentResolverActivity,
                        MyContentProvider> {

    private static final String TEST_CASE_1 = "test case 1";
    private static final String TAG = "MyMockContentResolverActivityTests2";

    public MyMockContentResolverActivityTests(String name) {
    super(MyMockContentResolverActivity.class.getPackage().getName(),
                        MyMockContentResolverActivity.class, MyContentProvider.class,
                        Samples.AUTHORITY);
    setName(name);
    }

    @Override
    public void createDatabaseFixture(Context mockContext) {
    final ContentResolver cr = mockContext.getContentResolver();
    cr.delete(Samples.CONTENT_URI, " 1 = 1 ", null);
    final ContentValues cv = new ContentValues();
    cv.put(Samples.NAME, TEST_CASE_1);
    final Uri uri = cr.insert(Samples.CONTENT_URI, cv);
    assertNotNull(uri);
    }

    public void testDatabaseFixture() {
    final Activity activity = getActivity();
    final ContentResolver cr = activity.getContentResolver();
    final Cursor c = cr.query(Samples.CONTENT_URI, new String[] { Samples._ID },
                        null, null, null);
    assertTrue(c.getCount() != 0);
    c.close();
    }
    public void testSimpleCreate() {
    final Activity activity = getActivity();
    assertNotNull(activity);
    final TextView tv = (TextView) activity.findViewById(
                        com.codtech.android.training.mockcontentresolver.examples.R.id.TextView01);
    assertNotNull(tv);
    }

    public void testDatabaseTestCase1() {
    final Activity activity = getActivity();
    assertNotNull(activity);
    TextView tv = (TextView) activity.findViewById(
                        com.codtech.android.training.mockcontentresolver.examples.R.id.TextView01);
    assertNotNull(tv);
    assertEquals("Cursor returns 1 row\n" + TEST_CASE_1, tv.getText().toString());
    sendKeys(KeyEvent.ACTION_DOWN);
    }
    }



    createDatabaseFixture

    Firstly, the database is cleaned up to start the test from a know base. Strictly speaking this could not be necessary but we prefer to err on the safe side.
    Secondly, a single row is inserted to materialize our fixture.
    Remember this is an abstract method in ActivityAndContentProviderInstrumentationTestCase2.

    testDatabaseFixture

    Simple test, just to verify that our database fixture is correct.

    testSimpleCreate

    Verify that our Activity has been created correctly and everything is in place.

    testDatabaseTestCase1

    Functional test, we verify the data presented in the Activity, check that the information is the expected one and finally we send key events simulating user interaction.
    In this test we are not using the ContentResolver nor the database directly and we see everything from the Activity's perspective.





    conclusion

    Though this is an oversimplified example it shows the most important concepts behind android-mock library classes and how you can implement your tests relying on it.
    Comments are gladly welcome.






    Copyright © 2009 Diego Torres Milano. All rights reserved.

    Monday, December 07, 2009

    Android Testing: External libraries

    We are now applying all the techniques discussed in previous posts to test our android projects.

    However, all the projects reviewed were so simple that no one used external libraries so we never faced the problem of using them from our tests.

    In this example we are using a dummy external library we have called libdummy-0.0.1-SANPSHOT.jar. In our Activity we are creating an object from one of the library's classes.

    When we write the tests and we need to resolve the references to that objects we may feel tempted to include libdummy in test project's external jars, but doing so

    [2009-12-07 16:01:14 - TCTDD11Test]Launching instrumentation android.test.InstrumentationTestRunner on device emulator-5554
    [2009-12-07 16:01:25 - TCTDD11Test]Test run failed: Class ref in pre-verified class resolved to unexpected implementation
    [2009-12-07 16:01:25 - TCTDD11Test]Test run complete


    we receive the previous error.

    The solution is to export the external jar from the main project. Open project Properties and edit Java Build Path to include the external library



    and export it to be contributed to dependent test project



    and this will cause no problems running the tests.

    Update: Android Compatibility library: Testing Fragments
    The aforementioned solution can also be applied to the android-support.jar, the library needed to support Fragments in pre-Honeycomb versions. As before, this should be exported in the main project and used in tests.


    Hope this helps.


    Update: Using ant
    Sometimes you may want to use ant to build your projects, for example when you are building using a Continuous Integration server like Hudson, Jenkins, Bamboo, CruiseControl or similar.
    To achieve this goal the easiest way is to update the main and test projects using the android update project and android update test-project commands.
    The missing step using this approach is the inclusion of the external libraries we used in our previous examples. In the libs directory of the main project include the used libraries. It's preferable to create symbolic links so they are automatically updated if you update the libraries by any mean.
    This is how the libs directory will look like:


    $ ls -l libs
    total 16
    lrwxr-xr-x 1 diego staff 82 1 Aug 18:13 android-support-v4.jar -> ../../../../opt/android-sdk/extras/android/compatibility/v4/android-support-v4.jar
    lrwxr-xr-x 1 diego staff 46 1 Aug 18:29 libdummy-0.0.1-SNAPSHOT.jar -> ../../libdummy/lib/libdummy-0.0.1-SNAPSHOT.jar

    Having done so, you will be able to create you application using ant debug and run the tests by ant run-tests and the libraries will be automatically included in the test project.

    Friday, December 04, 2009

    Android Testing at Droidcon London 2009


    I would like to thank Skills Matter and Novoda for the invitation to present the Android Testing tutorial at Droidcon London 2009.
    A very successful event indeed, and a lot of interesting talks were presented.
    Presentation slides and other resources can be found at http://android.codtech.com/droidcon2009 or if you prefer at slideshare.

    I would also thank Okpala Ikenna N. Jr., who took this and other great pictures of the event that can be seen at flickr.

    android-fixperms updated

    Android SDK 2.0.1 was released including minor API changes, bug fixes and framework behavioral changes. For information on changes and fixes, see the Framework API section.
    If you install Android SDK in a multi-user environment or in a continuous integration server, as was showed in previous posts, you should correct the permissions after updating. That is android-fixperms's raison d'être and as usual, it was updated to support this new SDK version.

    Find more information and examples at http://sites.codtech.com/android/android-tools.

    Friday, November 27, 2009

    Save the date: Android Testing at Droidcon London 2009

    Mark your calendars. On December 2, 2009, 17:00 I will be presenting Testing on Android a presentation that gives an introduction to Test Driven Development and Behaviour Driven Development on the Android platform, techniques that every serious development project should at least consider.

    More information at Droidcon London 2009