So, a while back one of the top Groovy developers released a version of Groovy that was ready to run on Android. Even beyond that, it comes with it's nice own Gradle plugin so all you really have to do is add the plugin to build and wa-la! You're good to go!

Now, I would like to say first off that I do sincerely believe that using Groovy would drastically speed up development time. Being able to use closures is great, and I'm a huge fan of dynamic languages (I've spent a lot of time in Python development). Even the New York Times is getting on board.

Unfortunately, it's simply not a possibility for me.

Because of the 64k method limit

So why won't I be using Groovy? Simply put, the 64k method limit for Dex.

This is a well documented issue in many Android projects. The crux of the issue is this: the Android format only supports being able to call 65,536 methods in an application. And while you likely won't write anywhere near that many methods yourself, trying to depend on external libraries fast approaches that limit. So far as I can tell, there are 4 solutions to addressing this problem:

Proguard

Proguard is a code obfuscation tool, that also uses static analysis to do some cool things. For example, Proguard can locate unused methods and strip them out of your application. Sounds nice, right? There are two problems.

  1. Proguard takes a while to run. Even if it's only 30 seconds, that's an extra 30 seconds per build that you lose in development time. It adds up and its frustrating to boot.

  2. Proguard isn't that great at removing methods. Proguard can only remove methods that aren't referred to by anyone else. So if you have a method that is unused during the application, but can still be reached, Proguard can't remove it. This is an issue especially in the Play services library - many methods will never be used, but Proguard can't guarantee that.

While eventually I'll be using Proguard for my release builds, in the mean time it would be painfully slow to wait 30 seconds every time to debug a build on my device.

Dynamic Classloading

Google put up a blog post detailing how you can use Dynamic Classloading to circumvent the 64k method limit. The basic workflow is this:

  1. Build a .dex file separate from your main application - this can contain an external library or whatever else is needed. This should be stored in your assets/ folder.

  2. Use code to load the external .dex file into memory

  3. This is where it gets interesting. To actually use this new Dex file you must either get classes via reflection and bind them to interfaces (such that the interface contains the methods you need), or call everything via reflection.

But if you can accomplish all this, you're done. That said, while this works, it's practically impossible to write maintainable code with this. There's an incredible amount of reflection involved and needed to make this work, and can not be accomplished in Eclipse because it doesn't support the build complexity. This is hardly a solution.

Native Interfaces

While I won't be investigating this one much, a similar solution would be to write as much of the code as possible in a native language (i.e. C++, C, etc.) and then call into that code using Java. Arguably this runs into similar issues as the Dynamic Classloading, but at least there's an API for it.

Alternatively, you can write Javascript for a WebView component and outsource most of your execution. The problem with this is you lose an incredible amount in terms of memory and performance. So while it would theoretically work, it would end up being painfully noticeable.

Use fewer libraries

This unfortunately seems to be the most practical solution. I haven't done any benchmarking, but I'd be willing to guess the time it takes you to write the code yourself is less than the time it would take to implement a solution above. While it forces you to write lower-level code, it's not anywhere near the point of trying to use reflection to load a library at runtime.

Unfortunately you do become very limited in what external libraries you can use. There are a great number of productivity-boosting libraries and things available, but sacrifices have to be made.

Summary

The 64k method limit in Dex is painful. And before you think that you'll never get anywhere near that limit, think about this:

  • The Google Play Services library itself contains 20 thousand methods. And you can't just use part of it. If you depend on Play services at all, one-third of your method count disappears

    • To be fair, people have invented ways of stripping out components you don't need. But this is still ridiculous.
  • The Groovy language implementation for Android uses roughly 30 thousand methods. A full half of your method count is immediately gone if you try and use Groovy.

So while both Play Services and Groovy provide some great features, it's basically one or the other. You can't have both. The 64k method limit is a big problem, and prevents people from using some of the great technology available. While I understand this is a technical nightmare to solve, there isn't really an "official" solution from Google to work around this. So, due to practical concerns, I won't be using Groovy. Much as I'd like to.

P.S.

If you're interested to see how many methods are used in your application, check out the script over here. It's a fantastic resource.

Finally, I've attached below a method count for a bare-bones app compiled to use Groovy. Enjoy!

Groovy Method Count

Read in 31289 method IDs.

<root>: 31289
    android: 5
        app: 3
        view: 2
    com: 19
        example: 17
            bspeice: 17
                testgroovy: 17
        thoughtworks: 2
            xstream: 2
    groovy: 3483
        beans: 111
        grape: 430
        inspect: 21
        io: 59
        lang: 1288
        security: 2
        time: 112
        transform: 265
            builder: 64
            stc: 59
        ui: 30
        util: 1153
            logging: 45
        xml: 12
    groovyjarjarantlr: 3072
        ASdebug: 7
        actions: 357
            cpp: 81
            csharp: 81
            java: 80
            python: 115
        build: 22
        collections: 129
            impl: 89
        debug: 393
            misc: 23
        preprocessor: 172
    groovyjarjarasm: 1319
        asm: 1319
            commons: 451
            signature: 41
            tree: 254
            util: 229
    groovyjarjarcommonscli: 219
    groovyjarjarharmonybeans: 174
        editors: 88
        internal: 10
            nls: 10
    groovyjarjaropenbeans: 815
        beancontext: 208
    java: 1342
        applet: 2
        awt: 51
            event: 6
        io: 179
        lang: 562
            annotation: 12
            invoke: 44
            ref: 12
            reflect: 82
        math: 41
        net: 54
        nio: 4
            charset: 4
        security: 18
        sql: 5
        text: 10
        util: 416
            concurrent: 32
                atomic: 13
                locks: 10
            logging: 13
            prefs: 12
            regex: 17
    javax: 74
        swing: 71
            event: 1
            text: 2
            tree: 2
        xml: 3
            parsers: 3
    org: 20767
        apache: 4
            commons: 4
                cli: 4
        codehaus: 20749
            groovy: 20749
                antlr: 2013
                    java: 304
                    parser: 490
                    treewalker: 943
                ast: 2269
                    builder: 660
                    expr: 500
                    stmt: 150
                    tools: 156
                classgen: 1620
                    asm: 842
                        indy: 30
                        sc: 118
                cli: 7
                control: 928
                    customizers: 436
                        builder: 195
                    io: 29
                    messages: 24
                plugin: 2
                reflection: 381
                    android: 3
                    stdclasses: 69
                runtime: 8887
                    callsite: 309
                    dgmimpl: 413
                        arrays: 190
                    m12n: 31
                    memoize: 38
                    metaclass: 463
                    powerassert: 30
                    typehandling: 731
                    wrappers: 30
                syntax: 160
                tools: 678
                    ast: 150
                    gse: 24
                    javac: 104
                    shell: 53
                        util: 38
                transform: 3194
                    sc: 107
                        transformers: 61
                    stc: 585
                    tailrec: 1160
                    trait: 90
                util: 395
                vmplugin: 197
                    v5: 46
                    v6: 1
                    v7: 140
        fusesource: 7
            jansi: 7
        xml: 7
            sax: 7
                helpers: 1

So you don't make the same mistakes.

Over the past couple of weeks I re-built MinimalBible fully on top of Android Studio. Previously when starting this project, I used Eclipse for a couple reasons. First being Eclipse was what was currently used in production. Second, the foundational library I rely on (jSword) was an existing Java library, so I would have to engineer the Gradle build myself to make it compatible with Android Studio.

Now all this was well and good, but I eventually wanted to migrate to Android Studio. It was new and robust, could do cool things, didn't have to mess with plugins, etc. After having used Android Studio for a while, it seems like most reputable libraries are moving there. Plus, IntelliJ is simply a better IDE than is Eclipse.

However, the migration process ended up being fairly challenging. I spent days migrating the jSword build to Ant, plus figuring out the random paths in the build.gradle file, plus the apt library for annotation processing... But the straw that broke the camel's back was testing. I had so many problems figuring out how to make Unit Tests work, I eventually gave up. It was time for a re-design, and I'm glad I did.

So, MinimalBible was built from the ground-up all over again. It ended up not being nearly as time-consuming or complicated as I expected. But, here's some of the lessons I've learned in the process. The idea is that nobody should have to make these errors again.

Lesson 1: Keep testing in mind

One of the things I was looking forward to in switching to Android Studio was getting testing working. The way I had originally structured the application, it was borderline impossible to use Mock objects. Here's the problem in a nutshell:

Problem:

  • Everything being injected needs to get access to the objects it is being injected with
  • Using the Application object seems like a good idea - it's available globally, so we can store all the dependencies there.
  • There's no way to swap out the Application instance during testing to provide mock objects. And any modifications done to the instance (like forcibly over-riding objects) are global - it becomes impossible to guarantee a consistent environment.

Solution:

Build in testing from the start. It's been really helpful for me to be able to change code and still have a sanity check to make sure I didn't break anything. My code coverage right now is still terrible, but I have a platform to make sure I can actually do this going forward.

Lesson 2: Static objects are awful

Every enterprise application I've worked on, and most Android applications, all have a dependency injection system of some form. I've previously outlined a number of them, but I settled on Dagger.

Problem:

  • Many times you need to ensure that only one instance of an object exists during execution
  • Add a static method to retrieve an instance of the object - everyone goes through the static method
  • This guarantees testing is impossible - you can't ever change the static method everyone else relies on. Thus, you can test that method, but never change it.

Solution:

Use your dependency injector the way it was meant to be used. I'm talking as few static references to anything as possible. Two examples of how I do this:

Configuration as code:

Both modules provide a singleton list that is used as configuration. It will not change during the application lifecycle, and I probably won't touch it again during development. But if I want to test how the objects inside the list are used, I can. This isn't possible if the "valid categories" are a static list.

MainConfig.java:

@Module()
class MainConfig {
    @Provides @Singleton
    List<BookCategory> provideValidCategories() {
        List<BookCategory> categoryList = new ArrayList<BookCategory>();
        categoryList.put(Category1);
        categoryList.put(Category2);
        return categoryList;
    }
}

TestConfig.java:

@Module()
class TestConfig {
    @Provides @Singleton
    List<BookCategory> provideValidCategories() {
        List<BookCategory> categoryList = new ArrayList<BookCategory>();
        categoryList.put(mockCategory1);
        return categoryList;
    }
}

Dynamic Injection references:

I've been talking a lot about testing, and here's some more. Bear with me.

One of the problems I ran into during testing was how objects being injected got access to the graph of their dependencies. Previously, I would call something like MyApp.inject(this), relying on a static reference to MyApp. Instead, each object should be given a interface that they can inject from, and you can worry about the interface implementation elsewhere. Consider the below:

MyFragment.java

class MyFragment extends Fragment() {
    public static MyFragment newInstance(Injector i) {
        MyFragment f = new MyFragment();
        i.inject(f);
        return f;
    }
}

MyActivity.java

class MyActivity extends Activity implements Injector {
    // We're going to assume that the ObjectGraph is created
    // elsewhere
    private ObjectGraph mObjectGraph;
    private void inject(Object o) {
        mObjectGraph.inject(o);
    }
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        MyFragment f = MyFragment.newInstance(this);
    }
}

MyActivityTest.java

class MyActivityTest extends TestCase implements Injector {
    // Again, created elsewhere
    private ObjectGraph mObjectGraph;
    private void inject(Object o) {
        mObjectGraph.inject(o);
    }
   
    public void testMyFragment() {
        MyFragment f = MyFragment.newInstance(this);
        // Actually do the testing stuff...
    }
}

Injector.java

interface Injector {
    public void inject(Object o);
}

So now after all this code we have a Fragment that can be injected by both the actual activity, or run in isolation with a TestCase. Nice.

Lesson 3: Design choices matter

Probably the most important lesson I can convey: If you see a bad pattern, now is the time to fix it. There were a number of different things that this re-platform allowed me to fix, and apply lessons that I've learned. And working in enterprise, I've seen how challenging it is to refactor code that's stuck in a broken design pattern. Every bit of time put in to make sure you start with good design pays incredible dividends.

So if you think that you have a better design idea, now's the time to make it happen. Don't let technological cruft build up, be ruthless about cutting that junk out. If you see a problem early, fix it. There are so many ways to get the point across, but it really is that important.

Conclusion

I learned a lot from re-implementing MinimalBible, and it's been great being able to re-think and start fresh. Plus, catching these things early makes life so much easier later on.

Hope this is helpful for making sure you can avoid issues in the future!

Scoped ObjectGraphs to the max

Technical Disclaimer: The technique detailed below is inspired by the Mortar library developed by Square. One of the things Mortar accomplishes for you is allowing scoped ObjectGraphs to be tied to your activity, meaning that when the Activity dies, the things in the ObjectGraph can be garbage collected. And when the screen is rotated, you don't have to re-create the ObjectGraph. I didn't want to use Mortar for my app, but re-created the technique. Check it out.

It's totally possible I'm not the first person to implement everything this way, but I still hope to claim naming rights anyway. I think I came up with a great name. Just so you know, "OGHolder" is the ObjectGraph Holder. But programmers need all the OG status they can get.

By any means, let me set out the problem:

Scoped ObjectGraphs are a feature of Dagger that allow you to do some nifty things. Essentially, the ObjectGraph is built in parts, rather than all at once. This means you have a root graph for your application, and each Activity has a graph that builds on top of this. There are a couple benefits:

  1. You don't have to build the entire ObjectGraph in one shot. Depending on the modules you inject, this can turn into a significant speed increase.
  2. When scoped graphs go out of scope, they are garbage collected - meaning you don't have to store all dependencies in memory for the entirety of an Application's lifecycle.

So, given that scoped graphs are pretty hot stuff, how do you go about doing it?

RootModules.java

@Module(
    library=true
)
class RootModules {
    MyApp application;
    public RootModules(MyApp application) {
        this.application = application;
    }
   
    @Provides @Singleton
    MyApp provideApplication() {
        return application;
    }
}

ActivityModules.java

@Module(
    injects=MyActivity.class,
    addsTo=RootModules.class // 1
)
class ActivityModules {
 
    @Provides @Singleton
    SomeService provideService(MyApp application) {
        return new SomeService(application);
    }
}

MyActivity.java

public class MyActivity extends Activity {
    @Inject
    SomeService someService;
   
    ObjectGraph mObjectGraph;
   
    public void inject(Object o) {
        if (mObjectGraph == null) {
            ObjectGraph root = ((MyApp)getApplication())
                    .getObjectGraph();
            mObjectGraph = root.plus(new ActivityModules()); // 2
        }
        mObjectGraph.inject(o); // 3
    }
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        inject(this); // 4
       
        // Do other Activity things here...
    }
}

Alright, time for some explanation.

  1. Our ActivityModules class declares itself as adding to the RootModules class. This means Dagger is expecting us to call .plus() to add ActivityModules to RootModules.
  • For the technically inclined, this is semantically different from @Module(includes=...), as the includes option tells Dagger to build the included graph at the same time as the root.
  1. Given that we get the root ObjectGraph from our Application, we now need to add the ActivityModules to it. The way this is done is by calling .plus() on the original graph, and storing the new graph.

  2. We're now ready to inject anybody that needs it, so inject people using the new scoped graph.

  3. Inject the SomeService object into the Activity so we can use it.

Given all this, we've now arrived at a working activity that can make use of SomeService. However, we do have an issue - whenever the screen is rotated, we'll create a new SomeService. If the service is marked as a @Singleton, why is it re-created when you rotate the screen?

Enforcing @Singleton

To understand why your singletons aren't actually singletons, you need a little picture of the Activity lifecycle. Try checking the documentation here. The important part is this:

Your activity will be destroyed and recreated each time the user rotates the screen.

So, let's break down what happens:

  1. Your initial Activity grabs the root ObjectGraph, creates a new graph using plus(), sets up SomeService, and then injects itself.

  2. You rotate the screen, and your Activity is destroyed, then re-created.

  3. The onCreate() method is called again now that the Activity is created - and when you go to inject again, you find that mObjectGraph is null again.

  4. So, we create another graph, with another SomeService, to inject with.

So, each ObjectGraph enforces the @Singleton annotation. However, if you create a new ObjectGraph, all bets are off. How do we combine scoped graphs and singletons then?

Introducing: The new OG

This section of the post is largely based on another fantastic tutorial. The basic premise is this: Fragments can be persisted across configuration changes. They can also hold on to Objects without a need to be serialized. Thus, store our ObjectGraph in the Fragment when it's created, and check there before we build another.

To demonstrate the principle, we need to add only a single class:

OGHolder.java

public class OGHolder extends Fragment {
    private final static String TAG = "OGHolder";
    private ObjectGraph mObjectGraph;
 
    // Use FragmentActivity for the support library
    public static OGHolder get(Activity activity) { // 1
        // Use getSupportFragmentManager for support library
        FragmentManager manager = activity.getFragmentManager();
        OGHolder holder = (OGHolder) manager.findFragmentByTag(TAG); // 2
        if (holder == null) {
            holder = new OGHolder();
            manager.beginTransaction().add(holder, TAG).commit();
        }
        return holder;
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true); // 3
    }
 
    public void persistGraph(ObjectGraph graph) {
        mObjectGraph = graph;
    }
 
    public ObjectGraph fetchGraph() {
        return mObjectGraph;
    }
}

And edit the Activity a little bit:

MyActivity.java

// ...
    public void inject(Object o) {
        if (mObjectGraph == null) { // 4
            // Check the holder to see if this is a restart
            OGHolder holder = OGHolder.get(this);
            mObjectGraph = holder.fetchGraph();
           
            // If the holder doesn't have a graph...
            if (mObjectGraph == null) {
                ObjectGraph root = ((MyApp)getApplication())
                        .getObjectGraph();
                mObjectGraph = root.plus(new ActivityModules());
                holder.persistGraph(mObjectGraph); // 5
            }
        }
        mObjectGraph.inject(this);
    }
    // ...

So let's get into the Secret Sauce:

  1. We have a static method that is responsible for looking up the OGHolder tied to this Activity. This method works because the FragmentManager is scoped to each Activity - we can have multiple OGHolder fragments with the same tag without worrying about collision.

  2. Get the OGHolder instance. If it doesn't exist, create a new one. When creating a new instance though, make sure to .commit()!

  3. This is the super-special magic piece of code. Long story short, this notifies Android to persist the fragment across restart.

  4. Now it's time to get our ObjectGraph. Get the OGHolder for this Activity, and then see if the holder's graph is null.

  5. If the holder's graph was null, we need to create a new one, and then tie it to the holder to persist it! Finally, inject, and then we're done.

And if you run the above code, you'll find that SomeService is only ever created once, since we don't lose the graph we created.

Summary

First things first, as detailed in the tutorial above, please don't use a different method for persisting objects across configuration change/screen rotate. This is the official way of doing things.

Second, while you can extend the technique to persist objects other than the ObjectGraph, strongly consider whether those objects shouldn't already be in the ObjectGraph anyway.

Finally, use this technique liberally. It allows you to be efficient both in terms of speed and memory usage. You only need to add a small Fragment and a couple lines to the injection. And in my case, I shaved off seconds of time processing because the expensive singletons didn't get re-created on screen rotate.

Hope this is helpful, feel free to contact me if you have questions!

Or, why testing on Android is nearly impossible

So recently I started work to re-design MinimalBible on the new Android Studio platform from the ground up (previously I had just ported the Eclipse version to Android Studio). The biggest impetus for this was me discovering there were significant issues in trying to test MinimalBible. Let's break down how the issues came up.

Dependency Injection

Using dependency injection (correctly) is awesome. It allows me to split apart pieces of code for easy testing, and especially with some of the legacy projects I've worked with, it would make life much easier. The framework I'm using for this is Dagger, a minimal-style JSR-330 compatible injector. One of the cool things about Dagger is that it validates the ObjectGraph (an object that stores references to everything that will need injecting) at compile-time, meaning a lot of really dumb errors I've made have been caught way ahead of time.

In actual operation, you need to store a reference to the built ObjectGraph, and the place that makes the most sense to do that is the Android Application. The Android ecosystem itself enforces that there will only be one application created, so store the graph there, and reference it as need be.

Now, one of the fundamental issues in testing is the use of mock objects. These are objects that look like the original, but the behavior is controlled during the test. So instead of an application getting connected to the CreditCardPaymentSystem, it receives a DummyPaymentSystem. It can still make calls to the payment system to simulate a purchase, but nobody will actually be charged.

However, the ObjectGraph-inside-Application can get problematic since the Singleton pattern carries with it a number of issues. There can only ever be one Application, but who is in control of that Application? If the Android OS is responsible for the Application, it becomes near-impossible to test - you can't change the ObjectGraph used to inject the rest of the application. There are three ways to address this that I've come up with so far.

Run-time re-inject

This was something I kind of invented myself. It's a really quick and easy way to implement the fixes needed, but is also most likely to lead to significant issues in testing.

The basic idea is this - expose the Application enough that you can add the mock objects you need at runtime. An example Application would look like this:

MinimalBible.java

public class MinimalBible extends Application {

    private ObjectGraph mObjectGraph;

    @Override
    public void onCreate() {
        mObjectGraph = ObjectGraph.create(new MinimalBibleModules());
    }

    public void inject(Object o) {
        mObjectGraph.inject(o);
    }

    public void plusObjGraph(Object[] modules) {
        mObjectGraph = mObjectGraph.plus(modules);
    }
}

MinimalBibleTest.java

public class MinimalBibleModule extends ActivityUnitTestCase<ClassUnderTest> {
    @Module(overrides = true)
    public static class MockModules {
        @Provides SomeObject provideObject() {
            return new MockObject();
        }
    }

    public void setUp() {
        // Assume you get a reference to the application under test somehow...
        mApplication.plusObjGraph(new MinimalBibleMockModules());
    }

    public void testModule() {
        // Test case code here
    }
}

In a typical flow, the Application would be started normally. Then, the test takes over to create the actual Activity. The first step is in the setUp(), where we re-create the ObjectGraph used by the app under test. We over-ride the existing objects with the mock implementation, and the Activity will never know the difference. Then the testModule() function can create the Activity, and everything is good to go!

Now, there are two problems with this: 1. We expose the Application un-necessarily to injection and over-riding. The whole idea of Dagger is that we can know what the ObjectGraph looks like ahead of time. Instead, this new model just kind of hacks around the problem in the first place. We would also have to make sure that nobody used the plusObjGraph() method outside of a test case, using it in actual code would be incredibly bad design.

  1. I can't prove that this would or would not be an issue, but: continuing to add on and over-ride objects multiple times would likely lead to issues. How does the graph respond to multiple overrides = true @Modules being used? Also, since tests can be executed without any regard to order, we are left with an inconsistent and unpredictable graph. The over-rides we were expecting to be there now aren't, and over-rides we weren't expecting now show up.

All told, this method works for a proof-of-concept, but is terrible design from the start.

Replace the Application Context

This technique comes from the dagger-unit-test project. Shout-out to vovkab for demonstrating this, I learned an incredible amount about both testing on Android and Dagger from this project.

The method itself involves a lot of under-the-hood shenanigans to make it work, but here's the basic idea:

MainActivityUnitTest.java

public class MainActivityUnitTest extends ActivityUnitTestCase<MainActivity> {
    public static final String TEST_HELLO_TEXT = "Test hello";

    private Application mApplication;
    private Context mContext;

    public MainActivityUnitTest() {
        super(MainActivity.class);
    }

    @Module(injects = MainActivity.class)
    final class TestModule {
        @Provides HelloController provideHelloController() {
            HelloController helloController = mock(HelloController.class);
when(helloController.getHello()).thenReturn(TEST_HELLO_TEXT);
            return helloController;
        }
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();

        mContext = new ContextWrapper(getInstrumentation().getTargetContext()) {
            @Override
            public Context getApplicationContext() {
                return mApplication;
            }
        };

        mApplication = new InjectableAppMock(mContext) {
            @Override public Object[] getModules() {
                return new Object[]{new TestModule()};
            }
        };

        setApplication(mApplication);
    }

    public void testHelloControllerValueSet() {
        setActivityContext(mContext);
        startActivity(MainActivity.createIntent(mContext), null, null);

        // Application context is AppMock
        assertTrue(getActivity().getApplicationContext() instanceof InjectableAppMock);

        TextView helloView = (TextView) getActivity().findViewById(R.id.hello);
        assertEquals(TEST_HELLO_TEXT, helloView.getText());
    }
}

Here's the basic premise of what's going on: 1. The Application objects, both mock and production, implement an interface with a simple getModules() method. This method returns the modules used to build the ObjectGraph - in the production case, it returns the production modules. The test case returns the modules used for mocking.

  1. The real shenanigans happen in the setUp() method. What this does is change the existing test Context. Specifically, it replaces the Application under test with a completely different application that implements the same getModules() method. This is how we get around the issues talked about earlier - we just run the tests inside an application we control, rather than the existing Application.

  2. Now we're prepared to run the actual test - we set the Context for the activity to the new one we control, start the Activity, and we're good to go.

This method does a good job of avoiding the problems with the first method. Specifically, we don't have to worry about people abusing the internals of the Application we created. Second, each Activity is tested in a completely isolated Application, so we don't have to worry about conflicts, etc. Finally, Dagger is free to do graph validation this way, since we declare the structure of the ObjectGraph at compile-time.

However, there are a couple things not so good about this:

  1. These tests require intimate knowledge of the way Android operates in order to be effective. If anything goes wrong, it's up to you to fix it, since you're really playing with fire.

  2. This is killer: you can only run unit tests with this style. Because this method relies on you controlling the Application lifecycle, you can not use classes like the ActivityInstrumentationTestCase2<>. The Instrumentation test gives Android control of the Context, Application, and Activity lifecycles, so we are unable to mock any objects used in a multi-activity test.

  3. Finally, and because of #2 above, Activities are the only objects you can test and mock. Specifically that means Fragments become untestable. Or at the very least, incredibly complicated to set up. I can't prove that it's impossible, but I spent a couple days trying to get Fragments working with this and had no luck. I did however receive plenty of incredibly cryptic errors.

Product Flavors

This final style of testing is inspired by U2020, but so far as I'm aware, my project is the first to implement/pioneer this. Product Flavors were introduced in the new Android build system (so they are Android Studio-specific) and allow you to mix and match different versions of the same class. So for example, one version of an Activity would show ads, and the second wouldn't.

What I do instead is define a testing flavor that defines the modules to inject for Dagger:

MinimalBibleApplication.java

public class MinimalBibleApplication extends Application {
    private ObjectGraph mObjectGraph;

    @Override
    public void onCreate() {
        super.onCreate();
        buildObjGraph();
    }

    public void buildObjGraph() {
        mObjectGraph = ObjectGraph.create(Modules.list(this));
    }

    public void inject(Object o) {
        mObjectGraph.inject(o);
    }

    public static MinimalBible get(Context ctx) {
        return (MinimalBible)ctx.getApplicationContext();
    }
}

This class still acts as a Singleton - everybody can come here to find the ObjectGraph, but the graph is build using the results of Modules.list(). The question then becomes - what the heck is Modules?

main/Modules.java

public class Modules {
    private Modules() {}

    public static Object[] list(MinimalBible app) {
        return new Object[] {
                new MinimalBibleModules(app)
        };
    }
}

test/Modules.java

public class Modules {
    private Modules() {}

    public static Object[] list(MinimalBible app) {
        return new Object[] {
                new MinimalBibleModules(app),
                new TestModules()
        };
    }
}

Here we are presented with two files of exactly the same name. How are we to tell which wone we are to use? That's where the Product Flavors come in. Since these files are stored in different directories (main vs. test) they are used by the Main and Test flavors respectively. Thus, when we test, use the Test flavor. Otherwise, use the Main flavor.

This style excels at a couple things:

  1. Incredibly clean design. You program the wiring separate from the Application itself.

  2. You don't have to mess with Android internals to enable testing. The only real difference is that you just add the TestModules() at test time, and they over-ride the main modules.

  3. Scalable - because you aren't messing with internals, and you're using the injection system the way it was intended, you won't have to worry about really strange errors. Trust me, there's a lot of really obscure things that can come up.

  4. You can actually test - the problem with touching the Android run-time is that you make the test cases intrinsically tied to the Application/Activity lifecycle. The first method only worked with instrumentation test cases (since it was assumed the Application had already been started for you to inject it), the second only with unit test cases (since you had to create your own Application). This way allows you to program to whatever style you need.

  5. Not Application level-specific, like the second method. The same rules apply there as here.

However, there are some legitimate challenges:

  1. Having to maintain different flavors for testing vs. production. You don't have a unified folder containing all the code. This is maybe a bit OCD of me, but I think it's frustrating to have the main code folder, test case folder, the production @Modules, and the mock @Modules in their own folders. Trying to keep everything in sync is a challenge (albeit much easier than the challenges presented by above methods)

  2. This only works for Android Studio. Product flavors are specific to Android Studio, and thus require other people to learn how your product flavors are used.

  3. Module conflicts - If different test cases need different mock modules, you are in royal trouble (the first method could theoreticaly get around this using scoped ObjectGraphs). I don't have a specific use-case in mind of why you would need this, but it will require some extra coding to make it work. The only thing to be done in this case is to add extra methods to the mock module specific to the test case.

Summary

After having spent some time starting from scratch trying to re-implement testing in MinimalBible, I'm grateful for the effort I put in. Finding these issues now instead of integrating testing later has certainly saved me an incredible amount of time and frustration. Plus, I learned enough about how the testing system works to understand how to write tests in the future.

One point needs to be made though - RoboGuice and other similar dependency injection frameworks won't have this issue, since your dependencies are created at run-time. The issues described above are mostly specific to Dagger (although I suspect Android Annotations would run into the same problems).

So all this said, let me take a moment to echo the concerns of an influential Android developer that testing on Android is awful right now. He's more so talking about test speed, while I've spent far more time trying to figure out how to make my tests work. Technically different concerns, but we're both frustrated with how testing is currently structured. And while I think it will end up serving me well, I was practically forced to re-structure my application to make this all work. I can only imagine how frustrating testing was before product flavors.

Thanks for sticking with me through the post, hope you learned a lot!

Hey all! Quick update:

I've been trying for the past few weeks to get unit testing enabled on the app. This unfortunately hasn't been going well, and there have been too many late nights on StackOverflow and Google with no luck.

The issues are entirely to do with Dagger and the Application lifecycle on Android. Currently, I need to inject mock objects into the ObjectGraph so that I can run the tests. Problem being, Dagger validates everything at compile time. Note that I'd have the same issues using any other dependency injection framework, it just happens that I'm using dagger right now. That being said, the only way to inject new objects is to plus() the original ObjectGraph and then store that back in the Application. I personally don't consider this acceptable - besides just being bad code (messing with the Application just doesn't sound like a great idea) I'm guessing I would run into issues later if multiple modules try to over-ride the inject for a class.

So, I'll be taking time to start fresh with Android Studio. Using Gradle is awesome (mostly for dependency management), but I need to rebuild the application from the ground up to fully take advantage of the new platform (i.e. build variants and the support library).

To that end, what I'm doing to make sure I don't run into this issue in the future is similar to what's in the U2020 app - inject the application itself. This way, each build variant (i.e. debug, release, and testing) is free to implement the application however they want. And, that application is then free to set up mock objects how/if it needs.

So this work will take a week or three I expect, at which point I'll be back to regular development.

Thanks for the patience, and see you on the other side of a beautiful codebase!