Mockito on Android step-by-step

One of my biggest frustrations with writing code for Android has been the fact that none of the current Java mocking frameworks work on Android’s Dalvik VM. I tried to address that myself by getting ScalaMock working on Android, but that’s been less successful than I hoped (primarily due to general problems with getting Scala working in the Android environment).

Happily there’s been a major breakthrough recently – Mockito, one of the established Java mocking frameworks now supports Android :-) In this article, I’ll go through getting an Android application up and running with tests written using Mockito.

This is an Android version of the example in Martin Fowler’s article Mocks Aren’t Stubs. The code is checked into GitHub here. You’ll need to have the Android SDK and ADT Plugin for Eclipse installed to run this code.

We’re going to build a (very) simple ordering system. Orders will succeed if there’s enough inventory in our warehouse and fail if not. Let’s start by creating a very simple little Android application for us to test:

  1. Create a new Android project in Eclipse called WarehouseManager. Select the “Create a Test Project” option.
  2. The core abstraction is a warehouse, represented by a Warehouse interface:

    package com.example;
    
    public interface Warehouse {
        boolean hasInventory(String product, int quantity);
        void remove(String product, int quantity);
    }
  3. And here’s a very simple concrete implementation of Warehouse:

    package com.example;
    
    import java.util.HashMap;
    
    public class RealWarehouse implements Warehouse {
    
        public RealWarehouse() {
            products = new HashMap();
            products.put("Talisker", 5);
            products.put("Lagavulin", 2);
        }
    
        public boolean hasInventory(String product, int quantity) {
            return inStock(product) >= quantity;
        }
    
        public void remove(String product, int quantity) {
            products.put(product, inStock(product) - quantity);
        }
    
        private int inStock(String product) {
            Integer quantity = products.get(product);
            return quantity == null ? 0 : quantity;
        }
    
        private HashMap products;
    }
  4. We remove things from the warehouse by placing an Order:

    package com.example;
    
    public class Order {
    
        public Order(String product, int quantity) {
            this.product = product;
            this.quantity = quantity;
        }
    
        public void fill(Warehouse warehouse) {
            if (warehouse.hasInventory(product, quantity)) {
                warehouse.remove(product, quantity);
                filled = true;
            }
        }
    
        public boolean isFilled() {
            return filled;
        }
    
        private boolean filled = false;
        private String product;
        private int quantity;
    }
  5. We’ll need a UI to allow us to make orders, so modify main.xml to look like this:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
      <TextView  
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content" 
          android:text="Product:"
          />
      <EditText
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content"
          android:id="@+id/product"
          />
      <TextView
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content" 
          android:text="Quantity:"
          />
      <EditText
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content"
          android:id="@+id/quantity"
          />
      <Button
          android:layout_height="wrap_content"
          android:layout_width="wrap_content"
          android:text="Place order"
          android:onClick="placeOrder" />
    </LinearLayout>
  6. And finally, here’s the implementation of WarehouseManagerActivity:

    package com.example;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.Toast;
    
    public class WarehouseManagerActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            productEditText = (EditText)findViewById(R.id.product);
            quantityEditText = (EditText)findViewById(R.id.quantity);
        }
    
        public void placeOrder(View view) {
            String product = productEditText.getText().toString();
            int quantity = Integer.parseInt(quantityEditText.getText().toString());
            Order order = new Order(product, quantity);
            order.fill(warehouse);
    
            String message = order.isFilled() ? "Success" : "Failure";
            Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
            toast.show();
        }
    
        private Warehouse warehouse = new RealWarehouse();
    
        private EditText productEditText;
        private EditText quantityEditText;
    }

You should now have a little Android application that can be compiled and installed with ant install. Here’s what it looks like:

Screenshot

So, now that we’ve got something to test, let’s test it:

  1. The version of Mockito that supports Android has not yet been released, so you’ll need to get the source and build it for yourself. After building, you should have mockito-all-1.9.1-SNAPSHOT.jar in the target directory.
  2. Create a libs directory underneath WarehouseManagerTest and copy mockito-all-1.9.1-SNAPSHOT.jar into it. Download the latest dexmaker into the same directory.
  3. Finally, we can write our tests, which create mock instances of the Warehouse interface:

    package com.example.test;
    
    import android.test.InstrumentationTestCase;
    import com.example.*;
    
    import static org.mockito.Mockito.*;
    
    public class OrderTest extends InstrumentationTestCase {
    
        public void testInStock() {
            Warehouse mockWarehouse = mock(Warehouse.class);
    
            when(mockWarehouse.hasInventory("Talisker", 50)).thenReturn(true);
    
            Order order = new Order("Talisker", 50);
            order.fill(mockWarehouse);
    
            assertTrue(order.isFilled());
            verify(mockWarehouse).remove("Talisker", 50);
        }
    
        public void testOutOfStock() {
            Warehouse mockWarehouse = mock(Warehouse.class);
    
            when(mockWarehouse.hasInventory("Talisker", 50)).thenReturn(false);
    
            Order order = new Order("Talisker", 50);
            order.fill(mockWarehouse);
    
            assertFalse(order.isFilled());
        }
    }
  4. Run your tests :-)

18 Responses to “Mockito on Android step-by-step”


  1. 1 vips May 21, 2012 at 4:30 am

    Hi,
    I tried and am runing into a issue. I went and built the latest mockito 1.9.1 and placed the dexmarker in libs folder, but I run into
    Test code:-
    import android.test.AndroidTestCase;
    import android.test.InstrumentationTestCase;

    import static org.mockito.Mockito.*;

    public class Test extends InstrumentationTestCase{

    public void test() {
    AA conn1 = mock(AA.class);
    conn1.f();
    }
    }

    Trace:-
    java.lang.ExceptionInInitializerError
    at org.mockito.internal.creation.jmock.ClassImposterizer.createProxyClass(ClassImposterizer.java:85)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:62)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:56)
    at org.mockito.internal.creation.CglibMockMaker.createMock(CglibMockMaker.java:23)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:49)
    at org.mockito.Mockito.mock(Mockito.java:1245)
    at org.mockito.Mockito.mock(Mockito.java:1139)

    at java.lang.reflect.Method.invokeNative(Native Method)
    at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
    at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:537)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)
    Caused by: java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
    at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
    at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
    at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
    at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
    at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
    at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
    at org.mockito.cglib.proxy.Enhancer.(Enhancer.java:70)
    … 22 more

  2. 3 vips May 21, 2012 at 1:15 pm

    yes, your code works fine. I added my class in warehousemanager and I was able to mock.

    let me add some more.
    my target application package consists of an activity and service and what I am trying to do is mock a class which is used by service component. Now I get this error:- I have tried derviving my test class from TestCase/AndroidTestCase/InstrumentationTestCase/ServiceTestCase and used InstrumentaionRunner and it fails with this trace now:-
    Can you tell how I should go about testing my class which is part of service? Thanks in advance.

    java.lang.IllegalArgumentException: dexcache == null (and no default could be found; consider setting the ‘dexmaker.dexcache’ system property)
    at com.google.dexmaker.DexMaker.generateAndLoad(DexMaker.java:359)
    at com.google.dexmaker.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:252)
    at com.google.dexmaker.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:54)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:49)
    at org.mockito.Mockito.mock(Mockito.java:1245)
    at org.mockito.Mockito.mock(Mockito.java:1139)
    at com.citrix.vpn.pool.test.Test1.testConnect(Test1.java:20)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:537)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)

  3. 4 vips May 21, 2012 at 2:22 pm

    problem seems to be solved, it was due to a shared useid in manifest. :(

  4. 5 Igor Ganapolsky September 24, 2012 at 8:08 pm

    Hello,
    thank you for this tutorial. When I run the test application, I get the following 2 errors:
    java.lang.ExceptionInInitializerError
    at com.example.warehousemanager.test.OrderTest.testInStock(OrderTest.java:13)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
    at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)
    Caused by: java.lang.ExceptionInInitializerError
    at org.mockito.internal.MockitoCore.(MockitoCore.java:40)
    at org.mockito.Mockito.(Mockito.java:932)
    … 14 more
    Caused by: java.lang.ExceptionInInitializerError
    at org.mockito.internal.util.MockUtil.(MockUtil.java:21)
    … 16 more
    Caused by: org.mockito.exceptions.misusing.MockitoConfigurationException: Failed to load interface org.mockito.plugins.MockMaker using jar:file:/data/app/com.example.warehousemanager.test-2.apk!/mockito-extensions/org.mockito.plugins.MockMaker
    … 14 more
    Caused by: java.lang.ClassNotFoundException: com.google.dexmaker.mockito.DexmakerMockMaker
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
    at org.mockito.internal.configuration.ClassPathLoader.loadImplementations(ClassPathLoader.java:133)
    at org.mockito.internal.configuration.ClassPathLoader.findPlatformMockMaker(ClassPathLoader.java:102)
    at org.mockito.internal.configuration.ClassPathLoader.(ClassPathLoader.java:61)
    at org.mockito.internal.util.MockUtil.(MockUtil.java:21)
    at org.mockito.internal.MockitoCore.(MockitoCore.java:40)
    at org.mockito.Mockito.(Mockito.java:932)
    … 14 more

    AND

    java.lang.NoClassDefFoundError: org/mockito/Mockito
    at com.example.warehousemanager.test.OrderTest.testOutOfStock(OrderTest.java:25)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
    at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)

    I could not build the mockito jar like you said, so I took their latest jar and dropped it into the libs directory of my test project. Also, I used dexer-mockito jar file. Did I follow the right path?

    Thanks,
    Igor

  5. 6 paul September 24, 2012 at 9:25 pm

    Honestly, I have no clue. I know that the instructions here work – if you choose to take a different path, then I’m afraid that you’re on your own.

  6. 7 Sajit September 28, 2012 at 9:40 pm

    I have the same problem vips reported. Can you please let me know how you fixed it.

    My Android project needs to use a sharedUserId

  7. 8 Sajit September 28, 2012 at 9:50 pm

    Further, I did a little more googling and found this link

    https://groups.google.com/forum/?fromgroups=#!topic/mockito/Z2c71TqrdyA

    But adding Thread.currentThread().setContextClassLoader(getClass().getClassLoader());

    in the setUp method now gives a
    java.lang.VirtualMachineError
    at dalvik.system.DexFile.defineClass(Native Method)
    at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:211)
    at dalvik.system.DexPathList.findClass(DexPathList.java:315)
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:58)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
    at com.google.dexmaker.stock.ProxyBuilder.loadClass(ProxyBuilder.java:272)
    at com.google.dexmaker.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:254)
    at com.google.dexmaker.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:56)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:51)
    at org.mockito.Mockito.mock(Mockito.java:1243)
    at org.mockito.Mockito.mock(Mockito.java:1120)
    at com.wirelessgeneration.service.LoginServiceBinderTest.setUp(LoginServiceBinderTest.java:26)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1584)

  8. 9 Eric October 10, 2012 at 4:26 am

    Great article! I’ve found Mockito invaluable this past year (GWT project), and am excited to hear that there is a path to it for my potential upcoming Android work.

  9. 10 Rick October 17, 2012 at 6:31 pm

    @Sajid and @Paul

    I’m running into the same problem. Trying to mock an activity using mockit + dexmaker on an android vm – any ideas? would love to get mockito rolling with instrumentation tests that I have.. thx

    java.lang.VirtualMachineError
    at dalvik.system.DexFile.defineClass(Native Method)
    at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:207)
    at dalvik.system.DexFile.loadClass(DexFile.java:196)
    at dalvik.system.DexClassLoader.findClass(DexClassLoader.java:226)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:551)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
    at com.google.dexmaker.stock.ProxyBuilder.loadClass(ProxyBuilder.java:272)
    at com.google.dexmaker.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:254)
    at com.google.dexmaker.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:54)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:49)
    at org.mockito.Mockito.spy(Mockito.java:1324)
    at com.company.droid.helper.ActivitySandbox.setupActivity(ActivitySandbox.java:21)
    at com.company.droid.ui.fragment.signup.SignupFragmentTest.setUp(SignupFragmentTest.java:50)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:529)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1448)

  10. 11 odiggity January 16, 2013 at 7:33 pm

    I was having problems getting it working using this tutorial and it turns out I only had one of the two dexmaker jar files listed here: http://code.google.com/p/dexmaker/downloads/list

    So after adding:
    dexmaker-1.0.jar
    dexmaker-mockito-1.0.jar
    mockito-all-1.9.5.jar

    to my test project classpath it worked fine.

    Hope this helps

  11. 12 Alex Shyshkov April 29, 2013 at 3:03 am

    Dude, that’s a perfect example how NOT TO DO unit testing! That’s the point of writing tests after your app is done and deployed to a device? At this point YOU ARE DONE. Instead, write the tests 1st!

  12. 13 Phlip (@Pen_Bird) October 17, 2013 at 11:35 pm

    Greetings, Paul. I am all over the map with this (see https://groups.google.com/forum/#!topic/mockito/7GcAjTn4F2Y and http://stackoverflow.com/questions/19407513/mockitoexception-failed-to-mock-class-com-example-example-test-mainactivity ).

    Do you think you could upgrade your WarehouseManagerTest example to use Android 4.3 instead of 2.2?

  13. 16 XYZ January 19, 2014 at 7:31 am

    I have no shared user id in my android manifest, but still seem to be getting this error:
    java.lang.IllegalArgumentException: dexcache == null (and no default could be found; consider setting the ‘dexmaker.dexcache’ system property)
    at com.google.dexmaker.DexMaker.generateAndLoad(DexMaker.java:359)
    at com.google.dexmaker.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:252)
    at com.google.dexmaker.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:56)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:26)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:51)
    at org.mockito.Mockito.mock(Mockito.java:1243)
    at org.mockito.Mockito.mock(Mockito.java:1120)
    at testAdd.test1(testAdd.java:25)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)

    Please help!


  1. 1 OCTO talks ! » Droidcon London 2012 Trackback on November 5, 2012 at 7:05 am
  2. 2 Open source tool for Mobile application testing(To be continued) | Raven's writing desk Trackback on May 19, 2013 at 2:47 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s





Follow

Get every new post delivered to your Inbox.

%d bloggers like this: