【问题标题】:Android + Robolectric - RuntimeException / InstantiationException in queryBuilder.query() in ContentProviderAndroid + Robolectric - ContentProvider 中 queryBuilder.query() 中的 RuntimeException / InstantiationException
【发布时间】:2013-04-22 21:05:28
【问题描述】:

我对仅使用 SQLite 的 ContentProvider 进行了大约十个测试; all pass 将通过 queryBuilder.query() 的两个保存在 Content Provider 的 query() 方法中。

正在测试的方法在实际应用程序中有效!

这适用于 API 17 r2 和 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;
}

这是回溯:

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

请问这是 Robolectric 的限制,还是我的错?非常感谢!

附:如果反射 API 将失败的类名放在 InstantiationException 消息中,那不是很梦幻吗?

【问题讨论】:

    标签: android sqlite junit instantiation robolectric


    【解决方案1】:

    我的项目也遇到了同样的问题,最终通过谷歌搜索和 U Avalos 的先前答案解决了这个问题

    1。创建自定义 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。将@Config 注释添加到您的测试中

    要使用自定义阴影类,可以使用robolectric2中的@Config注解

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

    参考

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

    【讨论】:

      【解决方案2】:

      Android API 16 引入了这种新方法(mtholdefer 提到过):

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

      截至 2013 年 7 月 25 日,roboelectric 不实施此方法。但是,它实现了一个不使用 CancellationSignal 的类似方法。将此方法添加到 ShadowSqlLiteDatabase 让我的问题消失了:

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

      (是的,您需要将 roboelectric 作为子模块。)

      【讨论】:

      • 其实你应该可以在测试中创建自定义影子类并在运行时绑定它。但是,我还没有弄清楚如何在 Roboelectric 2 中做到这一点
      【解决方案3】:

      我遇到了类似的问题,我认为问题在于 Robolectric 不支持 SQLiteQueryBuilder。您可以阅读更多关于 here 的信息。基本上,SQLiteQueryBuilder 使用的 rawQueryWithFactor() 方法不会在 ShadowSQLiteDatabase 中被覆盖。因此,游标返回为 null

      我刚刚开始通过 SQLiteDatabaseInstance.query 实例化我的游标,它与 Robolectric 完美结合。祝你好运,如果你找到更优雅的解决方案,请告诉我,我是否也可以测试我的 queryBuilders!

      【讨论】:

      • 我遇到了同样的问题。你介意分享一个代码sn-p吗?如果我理解正确,您重写了您的 Android 类以适应单元测试?
      • 再想一想,你为什么不直接实现那个方法呢?我正在使用从 GitHub 分叉的 Roboelectric 2,在实现了缺少的方法后,它可以工作了。
      • 不,我在测试类中创建了我的光标并跳过了测试使用 queryBuilder 的代码。这只是一个小方法,所以不值得麻烦。如果你使用得足够多,那么实现 rawQueryWithFactory 绝对值得。
      猜你喜欢
      • 2012-11-23
      • 2013-08-04
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多