Тестирование базы данных SQLite в Robolectric
Я пытаюсь протестировать простую базу данных SQLite с помощью Robolectric в приложении для Android. Я добавляю некоторые значения, но при их чтении возвращается 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 это работает так, как ожидалось. Я делаю что-то неправильно или не удается проверить базы данных SQlite с помощью Robolectric?
Ответы
Ответ 1
ПРИМЕЧАНИЕ. Этот ответ устарел. Если вы используете Roboletric 2.X, см. fooobar.com/questions/303353/...
Проблема заключается в том, что Robolectric SQLiteDatabase хранится только в
память, поэтому, когда вы вызываете getReadableDatabase или getWritableDatabase,
существующая база данных будет переопределена новой пустой базой данных.
Я работал с той же проблемой, и только решение, которое я нашел
было то, что мне нужно было развить проект Robolectric и добавить
ShadowSQLiteOpenHelper для сохранения базы данных, если один контекст задан двумя
раз. Однако проблема с моей вилкой заключается в том, что мне пришлось "отключить",
close() - функция, когда contex задается, потому что иначе
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
Robolectric 2.3 использует реальную реализацию SQLite вместо коллекции теней и подделок. Теперь тесты могут быть записаны для проверки реального поведения базы данных.
Ответ 3
Код, связанный с принятым ответом, не работает для меня; он может быть устаревшим. Или, может быть, моя настройка совсем другая. Я использую моментальный снимок Robolectric 2.4, который, похоже, не содержит ShadowSQLiteOpenHelper, если я ничего не пропустил. В любом случае, я понял решение. Вот что я сделал:
Теперь, в этот момент, я заметил, что локальный файл базы данных создается локально: после первого запуска теста, который использовал мой подкласс SQLiteOpenHelper, я продолжал получать SQLiteException при последующих тестах, потому что моя таблица уже существовала; и я мог видеть файл под названием "путь" и файл, называемый "path-journal", сидящий в моем локальном репозитории. Это смутило меня, потому что у меня создалось впечатление, что теневой класс использует базу данных в памяти.
Оказалось, что строка нарушения в классе тени:
database = SQLiteDatabase.openDatabase( "path", null, 0 );
как в getReadableDatabase(), так и в getWriteableDatabase(). Я знал, что настоящий SQLiteOpenHelper может создать базу данных в памяти, и, посмотрев на источник, чтобы посмотреть, как это сделать, я заменил приведенную выше строку следующим образом:
database = SQLiteDatabase.create( null );
после чего все работает. Я могу вставлять строки и читать их.
Надеюсь, это поможет кому-то другому.
Одна странная вещь, которая произошла со мной, которая не совсем связана с этим вопросом, но может помочь кому-то еще, заключается в том, что я также использую jmockit, и я использовал его в одном тесте в том же классе; но по какой-то причине это привело к тому, что мой пользовательский теневой класс не использовался. Я перешел на Mockito только для этого класса, и он отлично работает.