16

我注意到时不时会有一个关于使用 Robolectric 测试自定义 ContentProviders 的问题。然而,对于如何正确地做到这一点,从来没有一个具体而明确的答案。我偶然发现了两种不同的方法:

但是,这两种方法我都得到了 java.lang.InstantiationException。有一些 SO 帖子说这是由于 SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java) 没有在 Robolectric 中被覆盖(Android + Robolectric - RuntimeException / InstantiationException in queryBuilder.query() in ContentProvider)。

我想我的问题是 - 是否有任何首选的解决方法可以使测试 ContentProviders 成为可能。或者是否有任何其他方法比上述两种方法更好。

4

5 回答 5

12

这是对我来说效果很好的 Robolectric (v2.4) 测试:

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;

import org.joda.time.LocalDate;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver;

import co.tomup.app.db.DbSchema;
import co.tomup.app.db.TomupContentProvider;
import co.tomup.app.db.model.CalendarDay;
import co.tomup.app.db.tables.CalendarDayTable;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

@Config(emulateSdk = 18)
@RunWith(RobolectricTestRunner.class)
public class CalendarDayProviderTest {

    private ContentResolver mContentResolver;
    private ShadowContentResolver mShadowContentResolver;
    private TomupContentProvider mProvider;

    @Before
    public void setup() {
        mProvider = new TomupContentProvider();
        mContentResolver = Robolectric.application.getContentResolver();
        mShadowContentResolver = Robolectric.shadowOf(mContentResolver);
        mProvider.onCreate();
        ShadowContentResolver.registerProvider(DbSchema.AUTHORITY, mProvider);
    }

    @Test
    public void testInsertAndDelete() {
        // insert
        CalendarDay calendarDay = new CalendarDay();
        calendarDay.setId(1L);
        calendarDay.setDay(new LocalDate());
        calendarDay.setMoonPhase("new");
        calendarDay.setSunrise(1);
        calendarDay.setSunset(100);
        Uri insertionId = mContentResolver.insert(CalendarDayTable.CONTENT_URI,
                calendarDay.toSQLiteContentValues());
        Cursor cursorCheck = mShadowContentResolver.query(CalendarDayTable.CONTENT_URI,
                null, null, null, null);
        while (cursorCheck.moveToNext()) {
            CalendarDay calendarDayCheck = CalendarDay.fromSQLiteCursor(cursorCheck);
            assertEquals(calendarDay, calendarDayCheck);
        }
        assertTrue(cursorCheck.getCount() > 0);
        // delete
        mShadowContentResolver.delete(insertionId,
                null, null);
        cursorCheck = mShadowContentResolver.query(CalendarDayTable.CONTENT_URI,
                null, null, null, null);
        assertTrue(cursorCheck.getCount() == 0);
    }
}
于 2014-04-27T23:35:14.347 回答
6

您所要做的就是ShadowContentResolver在测试之前设置,因此它将正确地将您的权限ContentProviderContentProvider自身相关联。这是一个例子:

ShadowContentResolver.registerProvider(
        "com.example.provider",    //authority of your provider
        contentProvider    //instance of your ContentProvider (you can just use default constructor)    
);

最简单的方法是将这些东西放入一些@Before带注释的设置方法中。但是,更正确(因此长期而言更好)的方法是将其放入您的TestApplication#onCreate方法中,因此此配置将被应用程序中的所有测试使用。

于 2013-12-25T20:59:17.567 回答
3

使用 Roboelectric 4.2,这对我有用:

@RunWith(RobolectricTestRunner.class)
public class MyContentProviderTest {

    private ContentResolver contentResolver;
    private MyRepository repository;

    @Before
    public void setup() {
        repository = mock(MyRepository.class);
        contentResolver = ApplicationProvider.getApplicationContext().getContentResolver();

        ProviderInfo info = new ProviderInfo();
        info.authority = "my.authority";
        info.grantUriPermissions = true;
        ContentProviderController<MediaContentProvider> controller = Robolectric
                .buildContentProvider(MyContentProvider.class)
                .create(info);

        MyContentProvider provider = controller.get();
        // Method in my ContentProvider that allows to override
        // the repository it uses
        provider.setRepository(repository);
    }

    @Test
    public void testQuery() {

        // setup expectations in mockito
        when(repository.getAll()).thenReturn(...);

        try (Cursor cursor = contentResolver.query(SOME_URI, null, null, null, null)) {
            // test cursor contents here
        }

    }
}
于 2019-10-22T12:34:37.853 回答
0

我添加了一个 robolectic 的拉取请求,它添加了 ProviderTestCase2 的替换:https ://github.com/robolectric/robolectric/pull/2060 。

于 2015-09-24T19:58:45.773 回答
0

感谢Wim Deblauwe的回答,我也完成了Kotlin中的roboelectric 4.2 ,这是完整的演示:

这是我的自定义提供程序类

    //this is my custom provider class    
    public final class NameProvider : ContentProvider() {
        private var pContext: Context? = null;
        private var nameDbHelper: NameDbHelper? = null
        private var dataBase: SQLiteDatabase? = null
        private val Log_Tag = "NAME_PROVIDER"
        
        override fun onCreate(): Boolean {
            pContext = context
            nameDbHelper = pContext?.let { NameDbHelper(it) }
            dataBase = nameDbHelper?.writableDatabase
            if (dataBase == null) {
                Log.d(Log_Tag, "dataBase NOT created ...")
                return false
            }
            Log.d(Log_Tag, "dataBase created ...")
            return true
        }
    
        override fun query(p0: Uri, p1: Array<out String>?, p2: String?, p3: Array<out String>?, p4: String?): Cursor? {
            var queryBuilder : SQLiteQueryBuilder = SQLiteQueryBuilder()
            queryBuilder.tables = "TABLE_NAME"
            val cursor = queryBuilder.query(dataBase,p1,p2,p3,null,null, "COLUMN_NAME")
            cursor.setNotificationUri(pContext?.contentResolver, p0)
            return cursor
        }
    
        override fun insert(p0: Uri, p1: ContentValues?): Uri? {
            val rowID: Long? = dataBase?.insert("TABLE_NAME", "", p1)
            rowID?.let {
                if (it > 0) {
                    val _uri = ContentUris.withAppendedId(CONTENT_URI, it)
                    pContext?.contentResolver?.notifyChange(_uri, null)
                    return _uri
                }
            }
            throw SQLException("Failed to add a record into")
        }
        
        override fun getType(p0: Uri): String? {
            TODO("Not yet implemented")
        }
    
        override fun delete(p0: Uri, p1: String?, p2: Array<out String>?): Int {
            TODO("Not yet implemented")
        }
    
        override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int {
            TODO("Not yet implemented")
        }
    }

这是我的机器人电动测试课

    @Config(maxSdk = 29)
    @RunWith(RobolectricTestRunner::class)
    class NameProviderTest {
    
        private var contentResolver : ContentResolver? = null
        private var shadowContentResolver : ShadowContentResolver? = null
        private var nameProvider : NameProvider? = null
        
        @Before
        fun setUp() {
            contentResolver = ApplicationProvider.getApplicationContext<Context>().contentResolver
            val providerInfo : ProviderInfo = ProviderInfo()
            providerInfo.authority = NameContract.AUTHORITY
            providerInfo.grantUriPermissions = true
            val controller = Robolectric.buildContentProvider(NameProvider::class.java).create(providerInfo)
            shadowContentResolver = shadowOf(contentResolver)
            nameProvider = controller.get()
    
        }
    
        @Test
        fun onCreate() {
            val res  = nameProvider?.onCreate()
            assertEquals(res, true)
            //ShadowContentResolver.registerProviderInternal(NameContract.AUTHORITY, nameProvider)
        }
    
        @Test
        fun query() {
            val values =  ContentValues()
            values.put(NameContract.KEY_NAME, "ABCD")
            val uri = contentResolver?.insert(NameContract.CONTENT_URI, values);
            assertNotNull(uri)
            val cursor = contentResolver?.query(NameContract.CONTENT_URI,null,null,null,null)
            assertNotNull(cursor)
            var name : String? = null
            if(cursor != null && cursor.moveToFirst()){
                name = cursor.getString(cursor.getColumnIndex(NameContract.KEY_NAME))
            }
            cursor?.close()
            assertNotNull(name)
            assertEquals(name, "ABCD")
        }
        
        @Test
        fun insert() {
            val values =  ContentValues()
            values.put(NameContract.KEY_NAME, "Taher")
            val uri = contentResolver?.insert(NameContract.CONTENT_URI, values);
            assertNotNull(uri)
    
        }
    
    }
于 2021-12-24T12:34:03.217 回答