【问题标题】:Robolectric accessing database throws an errorRobolectric 访问数据库引发错误
【发布时间】:2016-01-14 22:30:35
【问题描述】:

我有一个测试,它创建了一个尝试从数据库中获取一些数据的活动。这会因 SQLiteException 而失败

17:40:40.528 [DEBUG] [TestEventLogger]     android.database.sqlite.SQLiteException: Cannot open SQLite connection, base error code: 14
17:40:40.528 [DEBUG] [TestEventLogger]      at org.robolectric.shadows.ShadowSQLiteConnection.rethrow(ShadowSQLiteConnection.java:53)
17:40:40.528 [DEBUG] [TestEventLogger]      at org.robolectric.shadows.ShadowSQLiteConnection.access$600(ShadowSQLiteConnection.java:30)
17:40:40.529 [DEBUG] [TestEventLogger]      at org.robolectric.shadows.ShadowSQLiteConnection$Connections.execute(ShadowSQLiteConnection.java:443)
17:40:40.529 [DEBUG] [TestEventLogger]      at org.robolectric.shadows.ShadowSQLiteConnection$Connections.open(ShadowSQLiteConnection.java:345)
17:40:40.529 [DEBUG] [TestEventLogger]      at org.robolectric.shadows.ShadowSQLiteConnection.nativeOpen(ShadowSQLiteConnection.java:58)
17:40:40.529 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteConnection.nativeOpen(SQLiteConnection.java)
17:40:40.529 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209)
17:40:40.529 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
17:40:40.529 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
17:40:40.530 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
17:40:40.530 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
17:40:40.530 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
17:40:40.530 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
17:40:40.530 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
17:40:40.530 [DEBUG] [TestEventLogger]      at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:1142)
17:40:40.530 [DEBUG] [TestEventLogger]      at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:267)
17:40:40.531 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:223)
17:40:40.531 [DEBUG] [TestEventLogger]      at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)

在我将数据库类移动到单例模型之前,这曾经可以正常工作。有什么建议应该如何使用 Robolectric 处理?我找不到这方面的任何文档或示例。

编辑:

运行 Robolectric 3.0 RC-2

Robolectric 正在推动我尝试对数据库进行一些工作的活动。 在我的应用程序数据库类中,从“修复”问题下方删除实例 == null 的检查(即,如果每次都重新创建 MySQLiteOpenHelper,Robolectric 运行测试没有问题)

public static synchronized MyDataManager getInstance(Context context){
    if (sInstance == null) {
        sInstance = new MyDataManager(context.getApplicationContext());
    }
    return sInstance;
}

private MyDataManager(Context context) {
    dbHelper = new MySQLiteOpenHelper(context);
}

MySQLiteOpenHelper 是 SQLiteOpenHelper 的简单扩展。

失败发生在(同样,这是在 db 类内部):

        database = dbHelper.getWritableDatabase();

显然,我真的不想每次都在我的应用程序中重新创建连接 - 我想没有人会想要这样吗?这让我觉得应该有一种方法可以在 Robolectric 中正确地做到这一点,而我只是在这里错过了一个技巧?

编辑:

另外,测试在隔离中成功运行,这让我觉得这与 Robolectric 在测试用例之间移动和重用数据库连接有关?

这两个测试都不做任何特定于数据库或与任何 DB 类相关的事情。第一个测试启动一个片段,该片段将访问数据库以写入一些数据。第二次测试在尝试打开数据库时失败。

【问题讨论】:

  • 向我们展示代码。什么版本的 robolectric?
  • 哪个代码有帮助?在失败/通过运行之间我没有太多改变 - 只是将我的 DB 类包装到一个单例模型中
  • 我看到你已经找到了:github.com/robolectric/robolectric/issues/1622。我总是要求提供代码以获得更好的理解。
  • @JaredBurrows 很公平。是的,很抱歉在这两个地方发帖-不确定这是一个错误还是我没有正确使用 Robolectric...如果有帮助,我已经添加了一些代码...
  • 不,它真的没有,因为你隐瞒了很多。您是否在使用 application 作为您的上下文?

标签: android android-sqlite robolectric


【解决方案1】:

在每次测试之间重置所有单例实例,否则你会得到像你这样的副作用。

@After
public void finishComponentTesting() {
    resetSingleton(YourSQLiteOpenHelper.class, "sInstance");
}

private void resetSingleton(Class clazz, String fieldName) {
    Field instance;
    try {
        instance = clazz.getDeclaredField(fieldName);
        instance.setAccessible(true);
        instance.set(null, null);
    } catch (Exception e) {
        throw new RuntimeException();
    }
}

【讨论】:

  • 你要去柏林参加机器人大会吗?很高兴认识你。关于答案 - 你知道这是错误的黑魔法:)
  • 我还没有门票,但我(或我的雇主)会订购一些。是的,很高兴见到你;)不,那不是wrong黑魔法,这只是单身人士的本性,迫使我们使用这种魔法;)
  • 酷,太好了!看看Dagger,你就会对这个黑魔法感到不安。基本上,客户端代码必须不知道自己的依赖关系在哪里、如何、谁和“单人”
  • 我从来没有研究过单例机制如何与 dagger 一起工作,因为我更喜欢 androidannotations。有没有解释这个的网站?我们计划使用弱引用的单例,但我不确定这是否会避免测试之间的手动重置。
  • 让我们谈谈会议。简短 - 我不喜欢使用 _ 的 AA“魔法”生成活动。您没有此代码,但应在 Manifest 中引用它。来自 Hugo 的 Sure apt 包括它们对 AS 可见的来源。但还是。使用 Dagger 的单人项目不是真正的单人项目,但很接近。弱参考不会使您免于手动重置
【解决方案2】:

IMO 您必须使用依赖注入来删除/替换数据库使用/单项,并在测试中模拟它们。在这种情况下,您不需要实例化代码/测试中未使用的内容。

听起来像是虚拟建议,并且比“仅仅修复当前状态”需要更多的努力。但我的经验是值得做的,它将导致整个应用程序的清晰设计和测试。

就我而言,它与(对于明显的例子再次抱歉):

  1. DebugUnit testing
  2. 修复 OEM 以防止内存泄漏

【讨论】:

  • 你的意思是在类中注入 SQLiteOpenHelper?这不会导致与数据库的多个连接吗?我的印象是应该只有一个 SQLiteOpenHelper
  • 我的意思是从 SQL 中抽象出你的模型并将这种抽象的东西注入到 UI 中
  • 我不确定这是否会起作用,因为下面仍然会有一个 SQLiteOpenHelper。或者我误解了你——你能举个例子吗?
  • 也许这篇文章会启发你medium.com/@artem_zin/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-30
  • 2021-12-11
  • 2021-01-19
  • 2012-07-18
  • 2021-12-29
  • 2019-02-01
  • 2013-03-18
相关资源
最近更新 更多