在Robolectric中测试SQLite数据库

时间:2011-09-06 13:36:28

标签: android sqlite robolectric

我正在尝试在Android应用程序中使用Robolectric测试一个简单的SQLite数据库。我正在输入一些值,但在读取它们时会返回0行。

我正在使用SQLiteOpenHelper类来访问数据库。

// RequestCache extends SQLiteOpenHelper
RequestCache cache = new RequestCache(activity); 
SQLiteDatabase db = cache.getWritableDatabase();

// Write to DB
ContentValues values = new ContentValues();
values.put(REQUEST_TIMESTAMP, TEST_TIME); 
values.put(REQUEST_URL, TEST_URL);
db.insertOrThrow(TABLE_NAME, null, values);

// Read from DB and compare values      
Vector<Request> matchingRequests = new Vector<Request>();
db = cache.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, SEARCH_URL_RETURN_COLUMNS, SEARCH_URL_WHERE, new String[] {url}, null, null, ORDER_BY, null);
int id = 0;

while(cursor.moveToNext()) {
    long timestamp = cursor.getLong(0);
    Request request = new Request(id++);
    request.setUrl(url);
    request.setCreationTimestamp(new Date(timestamp));
    matchingRequests.add(request);
}


// Assert that one row is returned
assertThat(matchingRequests.size(), equalTo(1));  // fails, size() returns 0

在robolectric之外调试代码时,这可以正常工作。我做错了什么或是不可能使用Robolectric测试SQlite数据库?

3 个答案:

答案 0 :(得分:19)

Robolectric 2.3 使用SQLite的真实实现,而不是阴影和假货的集合。现在可以编写测试来验证真实的数据库行为。

答案 1 :(得分:10)

注意:这个答案已经过时了。如果您使用的是Roboletric 2.X,请参阅https://stackoverflow.com/a/24578332/850787

问题在于Robolectric的SQLiteDatabase仅存储在 内存,所以当你调用getReadableDatabase或getWritableDatabase时, 现有数据库将被新的空数据库覆盖。

我遇到了同样的问题而且我发现了解决方案 是我需要分叉Robolectric项目并补充说 如果给出两个相同的上下文,则使用ShadowSQLiteOpenHelper来保存数据库 倍。然而我叉子的问题是我必须'禁用' close() - 当给出上下文时的函数,否则 Connection.close()将破坏内存中的数据库。我已经提出了拉取请求,但它尚未合并到项目中。

但随意克隆我的版本,它应该解决你的问题(如果我 理解正确:P)。 它可以在GitHub上找到:https://github.com/waltsu/robolectric

以下是如何使用修改的示例:

Context c = new Activity(); 
SQLiteOpenHelper helper = new SQLiteOpenHelper(c, "path", null, 1); 
SQLiteDatabase db = helper.getWritableDatabase(); 
// With the db write something to the database 
db.query(...); 
SQLiteOpenHelper helper2 = new SQLiteOpenHelper(c, "path", null, 1); 
SQLitedatabase db2 = helper2.getWritableDatabase(); 
// Now db and db2 is actually the same instance 
Cursor c = db2.query(...) ; // Fetch the data which was saved before 

当然,您不需要创建新的SQLiteOpenHelper,但这只是将相同的上下文传递给两个不同的SQLiteOpenHelper的例子。

答案 2 :(得分:2)

在接受的答案中链接的代码对我不起作用;它可能已经过时了。或许我的设置可能与众不同。我正在使用Robolectric 2.4快照,它似乎不包含ShadowSQLiteOpenHelper,除非我错过了什么。无论如何,我想出了一个解决方案。这是我做的:

  1. 我创建了一个名为ShadowSQLiteOpenHelper的类,并复制粘贴上面链接的代码(https://github.com/waltsu/robolectric/blob/de2efdca39d26c5f18a3d278957b28a555119237/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteOpenHelper.java)的内容,并修复了导入。
  2. 按照http://robolectric.org/custom-shadows/上有关使用自定义阴影的说明,我使用@Config( shadows = { ShadowSQLiteOpenHelper.class } )注释了我的测试类。无需自定义测试运行器。
  3. 在我的测试中,我实例化了我的SQLiteOpenHelper子类,传递new Activity()作为Context(Robolectric乐意照顾它),并且平常使用它。
  4. 现在,在这一点上,我注意到实际的数据库文件是在本地创建的:在第一次运行使用我的SQLiteOpenHelper子类的测试之后,我在后续测试中不断得到SQLiteException,因为我的表已经存在;我可以在我的本地存储库中看到一个名为“path”的文件和一个名为“path-journal”的文件。这使我感到困惑,因为我认为影子类使用的是内存数据库。

    事实证明,影子类中的违规行是:

    database = SQLiteDatabase.openDatabase( "path", null, 0 );
    
    getLadableDatabase()和getWriteableDatabase()中的

    。我知道真正的S​​QLiteOpenHelper可以创建一个内存数据库,在查看源代码以查看它是如何完成的之后,我将上面的代码替换为:

    database = SQLiteDatabase.create( null );
    

    之后,一切似乎都有效。我可以插入行并读回来。

    希望这有助于其他人。

    发生在我身上的一个奇怪的事情与这个问题并不完全相关,但可能会对某些人有所帮助,那就是我也在使用jmockit,而且我在同一个班级的一个测试中使用过它;但由于某种原因,这导致我的自定义阴影类不被使用。我转到了Mockito那个班级,它运作良好。