3

I'd like to write functional tests with Espresso for an Activity using a mock Retrofit API service instance created with a MockRestAdapter (https://github.com/square/retrofit/blob/master/retrofit-mock/src/main/java/retrofit/MockRestAdapter.java).

This is a bit tricky though, as you can't really ever inject any dependencies via the Activity's constructor.

Currently, a single Retrofit API service instance lives in my Application object, and I create a reference to it in each of my Activities' onCreate() methods.

How can I swap in a mock Retrofit API service? Perhaps Dagger is the answer?

Matt Logan
  • 5,886
  • 5
  • 31
  • 48
  • I was doing exactly this! Here is a post I wrote about it (it does not mention Espresso but it would be exactly the same apart from the test would contain Espresso api calls) See [Android Testing with Dagger, Retrofit & MockWebServer](http://systemdotrun.blogspot.co.uk/2014/11/android-testing-with-dagger-retrofit.html) – Dori Nov 22 '14 at 20:11

1 Answers1

6

Yes, Dagger is the answer. On how to achieve this, I recommend to look at Jake Wharton's u2020 at Github. In a nutshell, you provide different API implementations for production and debug flavors. Or, you could have a separate flavor for integration tests.

Another way to solve this without Dagger (adding support for it would be quite an undertaking for ongoing project), would be to have a flavor for integration tests. E.g. to have release, debug and mock flavors.

Then you could have a different Application class set in Android Manifest for mock flavor - an implementation which uses mock adapter. Core idea here is that you can override or augment you main AndroidManifest in flavors.

That'd roughly look like this:

Project structure

app/
    src/
       main/
           AndroidManifest.xml - Activities, services, permissions - all the stuff goes here 
           java/.../MyApplication.java
       mock/
           AndroidManifest.xml - example below
           java/.../MockApplication.java
       debug/    - Not relevant for this example
       release/  - Not relevant for this example

main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.android" >

    <!-- Permissions etc -->

    <application
        android:name=".MyApplication"
        ...
        >

        <!-- Activities, services etc -->

    </application>
</manifest>

mock/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:name=".MockApplication"/>
</manifest>

Application.java

public class MyApplication extends Application {

    @Override public void onCreate(Bundle savedState) {
        MyApi api = createApiAdapter();
    }

    protected MyApi createApiAdapter() {
        // Create regular Retrofit adapter
    }

}

MockApplication.java

public class MockApplication extends MyApplication {

    @Override protected MyApi createApiAdapter() {
        // Create mock Retrofit adapter
    }

}

build.gradle

android {

    // ...

    buildTypes {
        mock {
            applicationIdSuffix '.mock'
            versionNameSuffix '-mock'
            debuggable true
        }
        debug {
            applicationIdSuffix '.dev'
            versionNameSuffix '-dev'
            debuggable true
        }
        release {
            signingConfig signingConfigs.release
        }
    }
}
Eric Kamara
  • 17,717
  • 1
  • 15
  • 18
mindeh
  • 1,808
  • 14
  • 12
  • Thanks man! I didn't realize you could specify a different `Application` in the `AndroidManifest`. Could you give me a quick overview of how to do this? – Matt Logan Oct 24 '14 at 15:19
  • 2
    Added examples - pseudo code from top of my mind, but you should get the gist. Later I urge you to take a look at u2020 - much more elegant solution there with Dagger. But no coming back if once you start using it, in a good way;) – mindeh Oct 24 '14 at 16:42