Подтвердить что ты не робот

Тестирование базы данных 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?

4b9b3361

Ответ 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, если я ничего не пропустил. В любом случае, я понял решение. Вот что я сделал:

  • Я создал класс ShadowSQLiteOpenHelper и скопировал содержимое связанного выше кода (https://github.com/waltsu/robolectric/blob/de2efdca39d26c5f18a3d278957b28a555119237/src/main/java/com/xtremelabs/robolectric/shadows/ShadowSQLiteOpenHelper.java) и зафиксировал импорт.
  • Следуя инструкциям http://robolectric.org/custom-shadows/ для использования пользовательских теней, я аннотировал свой тестовый класс @Config( shadows = { ShadowSQLiteOpenHelper.class } ). Нет необходимости в настраиваемом тестовом бегуне.
  • В моих тестах я создавал экземпляр моего подкласса SQLiteOpenHelper, передавая в new Activity() как контекст (Robolectric к счастью позаботится об этом) и использовал его в обычном режиме.

Теперь, в этот момент, я заметил, что локальный файл базы данных создается локально: после первого запуска теста, который использовал мой подкласс SQLiteOpenHelper, я продолжал получать SQLiteException при последующих тестах, потому что моя таблица уже существовала; и я мог видеть файл под названием "путь" и файл, называемый "path-journal", сидящий в моем локальном репозитории. Это смутило меня, потому что у меня создалось впечатление, что теневой класс использует базу данных в памяти.

Оказалось, что строка нарушения в классе тени:

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

как в getReadableDatabase(), так и в getWriteableDatabase(). Я знал, что настоящий SQLiteOpenHelper может создать базу данных в памяти, и, посмотрев на источник, чтобы посмотреть, как это сделать, я заменил приведенную выше строку следующим образом:

database = SQLiteDatabase.create( null );

после чего все работает. Я могу вставлять строки и читать их.

Надеюсь, это поможет кому-то другому.

Одна странная вещь, которая произошла со мной, которая не совсем связана с этим вопросом, но может помочь кому-то еще, заключается в том, что я также использую jmockit, и я использовал его в одном тесте в том же классе; но по какой-то причине это привело к тому, что мой пользовательский теневой класс не использовался. Я перешел на Mockito только для этого класса, и он отлично работает.