在Robolectric中测试SQLite数据库

17
我想在我的Android应用中使用Robolectric测试一个简单的SQLite数据库。我插入了一些值,但是在读取它们时返回0行。 我正在使用SQLiteOpenHelper类来访问数据库。

我试图使用Robolectric在我的Android应用程序中测试一个简单的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数据库?


请检查这段代码是否正常工作???????http://stackoverflow.com/questions/6283834/sqlexception-when-using-google-analytics-with-robolectric-or-trying-to-use-sqli - Samir Mangroliya
3个回答

20

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


2
你能给我一些例子吗?与旧的测试代码相比,这在代码中是什么样子的? - Harvey Lin
这全是真的,只需在创建数据库时使用非 Mock 上下文,例如在使用 Roboelectric 运行时使用 RuntimeEnvironment.context。 - foo

10

注意:本回答已经过时。如果您正在使用Roboletric 2.X,请参见https://dev59.com/92w05IYBdhLWcg3wTwGY#24578332

问题出在Robolectric的SQLiteDatabase仅存储在内存中,因此当您调用getReadableDatabase或getWritableDatabase时,现有的数据库将被新的空数据库覆盖。

我也遇到了同样的问题,唯一找到的解决方法是需要fork Robolectric项目,并添加ShadowSQLiteOpenHelper来保存数据库,如果给定两个相同的context。然而,我的fork存在一个问题,即当给定context时,我必须“禁用”close()函数,否则Connection.close()会破坏内存中的数据库。我已经为此做了pull request,但它尚未合并到项目中。

但是请随意克隆我的版本,它应该可以解决您的问题(如果我理解得正确: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 会得到相同的数据库。


你知道我怎么能在预制数据库中使用它吗?我也在测试SQLite数据库,遇到了一个非常类似的错误,但我正在利用一个已经创建好的数据库。 - NioShobu
你能否从 OP 的代码中删除 db = cache.getReadableDatabase(); 这一行,以便重复使用初始引用? - Roger Keays
1
@RogerKeays:是的,没错。但我认为这段代码片段是简化版,否则仅测试Robolectric数据库代码的实现就没有意义。我认为写入和读取发生在不同的地方。 - Waltsu

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很高兴地处理了这个问题),并像往常一样使用它。

现在,在这一点上,我注意到一个实际的数据库文件正在本地创建:在使用我的SQLiteOpenHelper子类运行第一个测试后,由于我的表已经存在,我不断地获得SQLiteException;我可以看到一个名为“path”的文件和一个名为“path-journal”的文件坐落在我的本地存储库中。这让我感到困惑,因为我以为影子类正在使用内存数据库。

事实证明,影子类中有一个有问题的行:

database = SQLiteDatabase.openDatabase( "path", null, 0 );

在getReadableDatabase()和getWriteableDatabase()方法中都使用了SQLiteOpenHelper。我知道真正的SQLiteOpenHelper可以创建一个内存数据库,在查看源代码后,我将上述行替换为:

database = SQLiteDatabase.create( null );

之后,一切似乎都正常工作。我能够插入行并读取它们。

希望这能帮助其他人。

还有一件奇怪的事情发生在我身上,虽然与这个问题没有直接关系,但可能会对其他人有所帮助。我也使用了jmockit,在同一个类中的一个测试中使用了它;但由于某种原因,这导致我的自定义影子类未被使用。我只为那个类切换到Mockito,现在它可以正常工作。


嗨,Kevin,你的解决方案非常好,但是我发现我的SqliteOpenHelper中的onCreateDatabase(db)被异步调用了。你有遇到过这个问题吗?谢谢。 - thehayro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接