How to test content providers on Android
Asked Answered
F

4

11

I am trying to test my DB using ProviderTestCase2<T>. I can see the test DB being created. As such I suppose, the tested content provider should use the test DB. But as soon as I try any calls against the MockContentResolver (or the one created with newResolverWithContentProviderFromSql), I get an UnsupportedOperationException. This is documented for the MockContentResolver as normal behavior. As such I am a bit unsure on the purpose of the ProviderTestCase2.

How do you test your content providers?

Thanks

Fastigium answered 1/8, 2009 at 10:24 Comment(0)
A
3

Extend ProviderTestCase2 to override getMockContentResolver() and return your own class derived from MockContentResolver.

public class MyProviderTestCase2 extends ProviderTestCase2 {
    @Override
    public MockContentResolver getMockContentResolver () {
        return new MyMockContentResolver();
    }
}

MyMockContentResolver will need to override any methods you want to test in your ContentProvider.

Then you should be able to run any tests you want on your content provider while it's isolated by ProviderTestCase2

Aerothermodynamics answered 22/8, 2009 at 23:57 Comment(0)
T
13

As far as I found, setting up the mock content resolver is not explicitly necessary - I might oversee cases where it is(maybe correct resolving of the provider via URI, hings that need corect getType() working), but for me, it was enough to do something like this:

package org.droidcon.apps.template.provider.test;

import org.droidcon.apps.template.provider.ProfileContract;
import org.droidcon.apps.template.provider.ProfileProvider;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.ProviderTestCase2;

public class ProfileProviderTest extends ProviderTestCase2<ProfileProvider> {

    public ProfileProviderTest() {
        super(ProfileProvider.class, ProfileProvider.class.getName());
    }

    protected void setUp() throws Exception {
        super.setUp();
    }


    /**
     * Very basic query test.
     * 
     * Prerequisites: 
     * <ul>
     * <li>A provider set up by the test framework
     * </ul>
     * 
     * Expectations: 
     * <ul>
     * <li> a simple query without any parameters, before any inserts returns a 
     * non-null cursor
     * <li> a wrong uri results in {@link IllegalArgumentException}
     * </ul>
     */
    public void testQuery(){
        ContentProvider provider = getProvider();

        Uri uri = ProfileContract.CONTENT_URI;

        Cursor cursor = provider.query(uri, null, null, null, null);

        assertNotNull(cursor);

        cursor = null;
        try {
            cursor = provider.query(Uri.parse("definitelywrong"), null, null, null, null);
            // we're wrong if we get until here!
            fail();
        } catch (IllegalArgumentException e) {
            assertTrue(true);
        }
    }
}
Tardif answered 22/3, 2011 at 10:50 Comment(2)
There is not much sample code on the ProviderTestCase2 class on the internet. This is very helpful.Candlemas
In Android documentation, they recommend not to use directly your content provider and pass through a content resolver instead. Just read my post to see a more adequate example of how to test a content provider.Siouan
S
6

I add this entry as I think it can help programmers that want to test their Content Provider.

Imagine your Content Provider is called MyProvider and that you have a contract class called MyProviderContract defining some constants.

First of all, you'll write a test class called MyProviderTestCase that inherits from ProviderTestCase2<MyProvider>. You'll have to define a constructor which will call the super constructor:

public MyProviderTestCase() {
    super(MyProvider.class, MyProviderContract.AUTHORITY);
}

Then, instead of using directly your provider (avoid using getProvider() as users of your content provider won't access it directly), use the getMockContentResolver() to obtain a reference to a content resolver and then call the methods of this content resolver (query, insert, etc.). In the following code, I show how to test the insert method.

public void testInsert() {
    Uri uri = MyProviderContract.CONTENT_URI;
    ContentValues values = new ContentValues();
    values.put(MyProviderContract.FIELD1, "value 1");
    values.put(MyProviderContract.FIELD2, "value 2");
    Uri resultingUri = getMockContentResolver().insert(uri, values);
    // Then you can test the correct execution of your insert:
    assertNotNull(resultingUri);
    long id = ContentUris.parseId(resultingUri);
    assertTrue(id > 0);
}

Then you can add as many test methods as you want, using a content resolver instead of your content provider directly, as would do users of your content provider.

Siouan answered 6/3, 2013 at 13:26 Comment(1)
How to you run ProviderTestCase? Android unit test build variant or Instrumentation Test?Markle
A
3

Extend ProviderTestCase2 to override getMockContentResolver() and return your own class derived from MockContentResolver.

public class MyProviderTestCase2 extends ProviderTestCase2 {
    @Override
    public MockContentResolver getMockContentResolver () {
        return new MyMockContentResolver();
    }
}

MyMockContentResolver will need to override any methods you want to test in your ContentProvider.

Then you should be able to run any tests you want on your content provider while it's isolated by ProviderTestCase2

Aerothermodynamics answered 22/8, 2009 at 23:57 Comment(0)
I
1

[not directly related to question, but for future reference for whoever gets here looking for how to test content providers in android]

If working with API 28 or above, ProviderTestCase2 was removed from default classpath in Android SDK, so you need to manually add back in those classes in your build.gradle file.

android {

  //libraries added to classpath with useLibrary are being get from Sdk/platforms/android-XX/optional

  //adds ProviderTestCase2 to classpath from android.test package that comes with android SDK
  useLibrary 'android.test.runner'

  //adds AndroidTestCase to classpath from android.test package that comes with android SDK
  useLibrary 'android.test.base'

  //adds MockContentProvider to classpath from android.test.mock package that comes with android SDK
  useLibrary 'android.test.mock'

  //if you compiling against 27 or lower you do not need to add useLibrary calls above
  //only from api 28 above those classes were removed from the default classpath
  compileSdkVersion 30

}

Then you can just extend ProviderTestCase2 in your test case

package com.example.samplecontentprovidertest;

import android.test.ProviderTestCase2;

public class ExampleContentProviderTest extends ProviderTestCase2<ExampleContentProvider> {

public ExampleContentProviderTest() {
    super(ExampleContentProvider.class, ExampleContentProvider.AUTHORITY);
}

public void testUpdate() {
    int affectedRows = getMockContentResolver().update(ExampleContentProvider.SAMPLE_URI, null, null, null);
   //validate update
}

public void testDelete() {
    int affectedRows = getMockContentResolver().delete(ExampleContentProvider.SAMPLE_URI, null, null);
    //validate insert
}

}

working example: https://github.com/Artenes/android-content-provider-test-sample

Interface answered 25/7, 2021 at 3:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.