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


person Kjetil Eide    schedule 06.09.2011    source источник
comment
Проверьте, работает ли этот код???????/ stackoverflow.com/questions/6283834/   -  person Samir Mangroliya    schedule 06.09.2011


Ответы (3)


ПРИМЕЧАНИЕ. Этот ответ устарел. Если вы используете Roboletric 2.X, см. https://stackoverflow.com/a/24578332/850787.

Проблема в том, что SQLiteDatabase от Robolectric хранится только в памяти, поэтому при вызове 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 даст одну и ту же базу данных.

person Waltsu    schedule 04.02.2012
comment
Вы знаете, как я могу использовать это для готовой базы данных? Я также тестирую базы данных SQLite и столкнулся с очень похожей ошибкой, но я использую уже созданную базу данных. - person NioShobu; 23.08.2012
comment
Не могли бы вы просто удалить строку db = cache.getReadableDatabase(); из кода OP, чтобы исходная ссылка использовалась повторно? - person Roger Keays; 15.12.2012
comment
@RogerKeays: Да, верно. Но я считаю, что фрагмент кода был упрощенной версией, потому что в противном случае нет смысла просто тестировать реализацию кода базы данных Robolectric. Я думаю, что письмо и чтение происходят в разных местах. - person Waltsu; 28.12.2012

Robolectric 2.3 использует реальную реализацию SQLite вместо набора теней и подделок. Теперь можно писать тесты для проверки реального поведения базы данных.

person Jevgenij Evll    schedule 04.07.2014
comment
Не могли бы вы привести несколько примеров? Как это выглядит в коде по сравнению со старыми тестовыми кодами? - person Harvey Lin; 27.01.2017
comment
Это все правда, просто используйте контекст без издевательств для создания базы данных, например, RuntimeEnvironment.context при работе с Roboelectric. - person foo; 11.04.2018

Код, указанный в принятом ответе, у меня не сработал; он может быть устаревшим. Или, может быть, моя установка просто другая. Я использую моментальный снимок 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 в последующих тестах, потому что моя таблица уже существовала; и я мог видеть файл с именем «путь» и файл с именем «путь-журнал», находящиеся в моем локальном репозитории. Это смутило меня, потому что у меня сложилось впечатление, что теневой класс использует базу данных в памяти.

Оказывается, оскорбительная строка в теневом классе:

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

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

database = SQLiteDatabase.create( null );

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

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

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

person Kevin    schedule 02.07.2014
comment
Привет, Кевин, ваше решение отлично работает, однако я решил, что мой onCreateDatabase (db) в моем SqliteOpenHelper вызывается как-то асинхронно. Был ли у вас опыт решения этой проблемы? Спасибо. - person thehayro; 17.10.2016