Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android + Robolectric - RuntimeException / InstantiationException in queryBuilder.query() in ContentProvider

I have about ten tests on the ContentProvider that just uses SQLite; all pass save the two that pass through queryBuilder.query() in the Content Provider's query() method.

The methods being tested work in the real application!

This is with API 17 r2 and RoboLectric: robolectric-2.0-alpha-3-20130417.013705-46-jar-with-dependencies.jar

@Override
public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
    Log.d(Constants.TAG, "MyContentProvider.query()");
    switch(matcher.match(uri)) {
    case ITEM: // OK
        selection = "_id = ?";
        selectionArgs = new String[]{ Long.toString(ContentUris.parseId(uri)) };
    case ITEMS: // OK
        break;
    default:
        throw new IllegalArgumentException("Did not recognize URI " + uri);
    }
    // build the query with SQLiteQueryBuilder
    SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();
    qBuilder.setTables(TABLE_NAME);

    // query the database and get result in cursor
    final SQLiteDatabase db = mDatabase.getReadableDatabase();
    Cursor resultCursor = qBuilder.query(db,    // Line 112 in trace
            projection, selection, selectionArgs, null, null, sortOrder,
            null);
    resultCursor.setNotificationUri(getContext().getContentResolver(), uri);
    return resultCursor;
}

Here's the traceback:

java.lang.RuntimeException: java.lang.InstantiationException
    at org.robolectric.bytecode.ShadowWrangler.createShadowFor(ShadowWrangler.java:300)
    at org.robolectric.bytecode.ShadowWrangler.initializing(ShadowWrangler.java:74)
    at org.robolectric.bytecode.RobolectricInternals.initializing(RobolectricInternals.java:90)
    at android.database.sqlite.SQLiteQuery.$$robo$init(SQLiteQuery.java)
    at android.database.sqlite.SQLiteClosable.<init>(SQLiteClosable.java:26)
    at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:41)
    at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
    at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
    at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1314)
    at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:400)
    at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:333)
    at com.example.readingsprovider.ReadingsContentProvider.query(ReadingsContentProvider.java:112)
    at com.example.readingsprovider.test.ContentProviderTest.testUpdateMultipleWithoutWhere(ContentProviderTest.java:110)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:267)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:202)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.InstantiationException
    at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:30)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at java.lang.Class.newInstance0(Class.java:357)
    at java.lang.Class.newInstance(Class.java:310)
    at org.robolectric.bytecode.ShadowWrangler.createShadowFor(ShadowWrangler.java:293)
    at org.robolectric.bytecode.ShadowWrangler.initializing(ShadowWrangler.java:74)
    at org.robolectric.bytecode.RobolectricInternals.initializing(RobolectricInternals.java:90)
    at android.database.sqlite.SQLiteQuery.$$robo$init(SQLiteQuery.java)
    at android.database.sqlite.SQLiteClosable.<init>(SQLiteClosable.java:26)
    at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:41)
    at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
    at android.database.sqlite.SQLiteDirectCursorDriver.$$robo$$SQLiteDirectCursorDriver_7ac1_query(SQLiteDirectCursorDriver.java:44)
    at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java)
    at android.database.sqlite.SQLiteDatabase.$$robo$$SQLiteDatabase_ab15_rawQueryWithFactory(SQLiteDatabase.java:1314)
    at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java)
    at android.database.sqlite.SQLiteQueryBuilder.$$robo$$SQLiteQueryBuilder_ba4d_query(SQLiteQueryBuilder.java:400)
    at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java)
    at android.database.sqlite.SQLiteQueryBuilder.$$robo$$SQLiteQueryBuilder_ba4d_query(SQLiteQueryBuilder.java:333)
    at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java)
    at com.example.readingsprovider.ReadingsContentProvider.query(ReadingsContentProvider.java:112)
    at com.example.readingsprovider.test.ContentProviderTest.testUpdateMultipleWithoutWhere(ContentProviderTest.java:110)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    ... 21 more

Can plez tell if this is a limitation of Robolectric, or my bad? Many thanks!

P.S. Wouldn't it be dreamy if the Reflection API would put the failing class name in the InstantiationException message?

like image 635
idarwin Avatar asked Apr 22 '13 21:04

idarwin


3 Answers

I had the same issue for my projects, and was finally able to solve it with some googling and the previous answer of U Avalos

1. Create custom SQLiteShadow

@Implements(value = SQLiteDatabase.class, inheritImplementationMethods = true)
public class CustomSQLiteShadow extends ShadowSQLiteDatabase {

    @Implementation
    public Cursor rawQueryWithFactory (SQLiteDatabase.CursorFactory cursorFactory,
                                     String sql,
                                     String[] selectionArgs,
                                     String editTable,
                                     CancellationSignal cancellationSignal) {
      return rawQueryWithFactory(cursorFactory,
                                 sql,
                                 selectionArgs,
                                 editTable);
    }
}

2. Add @Config annotation to your test

To use the custom shadow class, you can use the @Config annotation in robolectric2

@RunWith(RobolectricTestRunner.class)
@Config( shadows = {CustomSQLiteShadow.class})
public class ContentProviderTest {

Reference

http://robolectric.blogspot.co.at/2013/05/configuring-robolectric-20.html

like image 103
Sukram Avatar answered Nov 01 '22 03:11

Sukram


Android API 16 introduces this new method (that mtholdefer mentions):

public Cursor rawQueryWithFactory (SQLiteDatabase.CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable, CancellationSignal cancellationSignal)

As of 7/25/2013, roboelectric does NOT implement this method. However, it implements a similar method that doesn't use a CancellationSignal. Adding this method to ShadowSqlLiteDatabase made the problem go away for me:

@Implementation
public Cursor rawQueryWithFactory (SQLiteDatabase.CursorFactory cursorFactory,
                                 String sql,
                                 String[] selectionArgs,
                                 String editTable,
                                 CancellationSignal cancellationSignal)
{
  return rawQueryWithFactory(cursorFactory,
                             sql,
                             selectionArgs,
                             editTable);
}

(Yes, you need to have roboelectric as a submodule. )

like image 24
U Avalos Avatar answered Nov 01 '22 03:11

U Avalos


I have been running into a similar issue and I believe the problem is that SQLiteQueryBuilder is not supported in Robolectric. You can read more about that here. Basically the rawQueryWithFactor() method that SQLiteQueryBuilder uses is not overridden in ShadowSQLiteDatabase. Because of this the cusor is returned as null

I just started instantiating my cursors via SQLiteDatabaseInstance.query and that works perfectly in conjunction with Robolectric. Good luck and let me know if you find a more elegant solution do I can test my queryBuilders as well!

like image 1
mtholdefer Avatar answered Nov 01 '22 02:11

mtholdefer