1

I am using Espresso to test an Activity that displays data retrieved using a ContentProvider.

I'd like to mock the content provider using MockContentProvider and MockContentResolver, but I don't know how to make the activity method getContentResolver() return a mock resolver.

(I could insert the data before executing the test, but I'd like to know if it is possible to use a mock provider instead)

UPDATE: I still could not make it work, so I'm including some code to try to make it clearer.

MyActivity.java

MyActivity extends AppCompatActivity {

    ...

    private delete(String id) {
       // The method getContentResolver() is what I want to mock when testing
       getContentResolver().delete(
           Contract.Item.makeUriForItem(id), null, null);
    }

    ...

}

MyActivityTests.java

@RunWith(AndroidJUnit4.class)
@LargeTest
public class StockChartDetailsActivityTests {

    @Rule
    public ActivityTestRule<MyActivity> myActivityTestRule = new ActivityTestRule<MyActivity>(
        MainActivity.class);

    @Test
    public void test() {

    }

}

I could not make the suggestion to use Dagger2 to work because getContentResolver() is implemented by android and I can't use @Inject to inject my own ContentResolver.

I could use different strategies:

  • inserting data in the database before testing.
  • have another layer, like a business logic layer, and move calls to getContentResolver() to the classes in this layer and then I could inject (and mock) the business logic classes.

But before using any of these strategies I'd like to know if it is possible to use MockContentResolver.

1 Answers1

2

You should use a Dependency Injection framework. I think that for Android a good choice is Dagger 2.

Using Dagger, you can inject in your Activity your real Content Provider. A common way for injecting something in an Activity, is to use a field injection (because you cannot act directly on the constructor of the Activity: it is created by the system). So you can instantiate the Dagger module in the Application class, and get it from the Activity in order to inject itself. Something like:

((MyApplication).getMyComponent()).inject(this);

Your class MyApplication will contain something like:

MyComponent mMyComponent = DaggerMyComponent.builder()
                .appModule(new AppModule(this)) // this is in general useful if you need a Context
                .contentProviderModule(new ContentProviderModule())
                .build();

And it has to include of course the getMyComponent() method for returning the module:

MyComponent getMyComponent() {
    return mMyComponent;
}

Now, that your module is injected in your Activity from the outside, you can easily inject something else for testing purpose:

In the /test/ folder and using the same path of your MyApplication class, you can create a new class that extends MyApplication and create your module passing a mocked version of the ContentProvider.

I cannot go through all the steps but you can take a look here for more details

GVillani82
  • 17,196
  • 30
  • 105
  • 172
  • Also check out Chiu-ki Chan's work. She uses Mockito and implements a custom runner to instantiate a mock application. http://blog.sqisland.com/2015/04/dagger-2-espresso-2-mockito.html https://youtu.be/JlHJFZvZyxw?t=23m38s – Paul Bruce Apr 06 '17 at 18:55
  • Can I inject something that is part of the Android framework, @GVillani82? What I'm trying to mock is the ContentResolver which I have access to by calling the method getContentResolver() on a context. – Rodrigo Carvalho Apr 07 '17 at 07:56
  • Yes, you need a Context. This is way I suggested you to use another module (AppModule) that inject the application context. [Here](https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2) you can find its implementation – GVillani82 Apr 07 '17 at 08:01
  • Good. Thanks a lot. – Rodrigo Carvalho Apr 07 '17 at 08:25