在 Robolectric 中测试 SQLite 数据库

发布于 2024-12-03 10:35:50 字数 1186 浏览 0 评论 0原文

我正在尝试在我的 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 数据库?

I'm trying to test a simple SQLite database using Robolectric in my Android application. I'm putting in some values, but when reading them back 0 rows are returned.

I'm using the SQLiteOpenHelper class to access the database.

// 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

When debugging the code outside robolectric this works as expected. Am I doing anything wrong or is it not possible to test SQlite databases using Robolectric?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

離殇 2024-12-10 10:35:50

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

Robolectric 2.3 uses a real implementation of SQLite instead of a collection of shadows and fakes. Tests can now be written to verify real database behavior.

葬﹪忆之殇 2024-12-10 10:35:50

注意:这个答案已经过时了。如果您使用的是 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 将给出相同的数据库。

NOTE: This answer is outdated. If you are using Roboletric 2.X, please see https://stackoverflow.com/a/24578332/850787

The problem is that Robolectric's SQLiteDatabase is stored only in
memory, so when you call getReadableDatabase or getWritableDatabase,
the existing database will be overridden with new empty database.

I was running to the same problem and only solution I found
was that I needed to fork the Robolectric project and added
ShadowSQLiteOpenHelper to save database if same context is given two
times. However the problem with my fork is that I had to 'disable'
close()-function when contex is given because otherwise
Connection.close() will destroy the database in memory. I have made pull request for it but it isn't merged to project yet.

But feel free to clone my version and it should fix your problem (If I
understood it correctly :P ).
It can be found on GitHub: https://github.com/waltsu/robolectric

Here is a example how to use the modification:

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 

Ofcourse you don't need to create new SQLiteOpenHelper, but that is just example that passing same context to two different SQLiteOpenHelper will give same database.

半边脸i 2024-12-10 10:35:50

已接受答案中链接的代码对我不起作用;它可能已经过时了。或者也许我的设置只是不同。我正在使用 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() 作为上下文(Robolectric 很乐意处理这个问题),并照常使用它。

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

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

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

在 getReadableDatabase() 和 getWriteableDatabase() 中。我知道真正的 SQLiteOpenHelper 可以创建一个内存数据库,在查看源代码了解它是如何完成的之后,我将上面的行替换为:

database = SQLiteDatabase.create( null );

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

希望这对其他人有帮助。

我身上发生的一件奇怪的事情与这个问题并不完全相关,但可能对某人有进一步的帮助,那就是我也在使用 jmockit,并且我在同一个班级的一个测试中使用了它;但由于某种原因,这导致我的自定义影子类无法使用。我就在那堂课上改用了 Mockito,效果很好。

The code linked in the accepted answer did not work for me; it may be out of date. Or perhaps my setup is just different. I am using Robolectric 2.4 snapshot, which does not seem to include a ShadowSQLiteOpenHelper, unless I missed something. In any case, I figured out a solution. Here's what I did:

  1. I created a class called ShadowSQLiteOpenHelper and copy-pasted the contents of the code linked above (https://github.com/waltsu/robolectric/blob/de2efdca39d26c5f18a3d278957b28a555119237/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteOpenHelper.java), and fixed the imports.
  2. Following the instructions on http://robolectric.org/custom-shadows/ for using custom shadows, I annotated my test class with @Config( shadows = { ShadowSQLiteOpenHelper.class } ). No need for a custom test runner.
  3. In my tests, I instantiated my subclass of SQLiteOpenHelper, passing in a new Activity() as the Context (Robolectric happily takes care of that), and used it per usual.

Now, at this point, I noticed that an actual database file was getting created locally: after the first run of a test that used my SQLiteOpenHelper subclass, I kept getting an SQLiteException on subsequent tests because my table already existed; and I could see a file called "path" and a file called "path-journal" sitting in my local repository. This confused me because I was under the impression that the shadow class was using an in-memory database.

It turns out the offending line in the shadow class is:

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

in both getReadableDatabase() and getWriteableDatabase(). I knew that the real SQLiteOpenHelper could create an in-memory database, and after looking at the source to see how it's done, I replaced the above line with:

database = SQLiteDatabase.create( null );

after which, everything seems to work. I am able to insert rows and read them back.

Hopefully this helps someone else.

One weird thing that happened to me that isn't exactly related to this question, but might help someone further, is that I'm also using jmockit, and I had used it in one test in the same class; but for some reason, this caused my custom shadow class to not be used. I switched to Mockito for just that class, and it works fine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文