Robolectric accessing database throws an error

久未见 提交于 2019-12-01 04:19:51

问题


I have a test that creates an activity which attempts to get some data from the database. This fails with 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)

This used to work fine before I moved my database class to a singleton model. Any suggestions how this should be handled with Robolectric? I couldn't find any documentation or samples on this.

EDIT:

Running Robolectric 3.0 RC-2

Robolectric is driving my activity which attempts to do some work with the database. In my app DB class, removing the check for instance == null from below 'fixes' the issue (i.e. there's no issue with Robolectric running the test if MySQLiteOpenHelper is recreated every time)

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 is a simple extension of SQLiteOpenHelper.

The failure is happening on (again, this is inside by db class):

        database = dbHelper.getWritableDatabase();

Obviously I don't really want to recreate a connection every time in my App - and I think nobody would want that? Which makes me think there should be a way to do this properly in Robolectric and I'm just missing a trick here?

EDIT:

Also, the test runs successfully in isolation, which makes me think it's something to do with Robolectric moving between the test cases and reusing the database connection?

Both tests don't do ANYTHING specific to database or related to any of the DB classes. First test starts a fragment which will access the database to write some data. Second test is failing on attempt to open the db as above.


回答1:


Reset all singleton instances between each test or you will get side effects like yours.

@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();
    }
}



回答2:


IMO you have to remove/replace database usage/singlethons with dependency injection and mock them in tests. In this case you don't need to instantiate things that are not used in your code/tests.

Sounds like dummy suggestion and something that requires more effort than "just to fix current state". But my experience it is worth to do and it will lead for clear design and testing for entire application.

As for me it is comparable to (sorry again for obvious examples):

  1. Debug vs Unit testing
  2. Fixing OEM with preventing memory leaks


来源:https://stackoverflow.com/questions/30308776/robolectric-accessing-database-throws-an-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!