Тестирование базы данных на Android: ProviderTestCase2 или RenamingDelegatingContext?

Я реализовал доступ к базе данных с помощью SQLiteOpenHelper из пакета android.database в некоторых классах (с шаблоном DAO).

Я написал несколько тестов junit для этих классов с помощью AndroidTestCase но это заставляет тесты использовать ту же базу данных, что и приложение.

Я прочитал, что ProviderTestCase2 или RenamingDelegatingContext можно использовать для тестирования базы данных отдельно. К несчастью, я не смог найти хороший учебник / пример, который показывает, как тестировать базу данных с помощью ProviderTestCase2 / RenamingDelegatingContext.

Может ли кто-нибудь указать мне где-нибудь или дать мне совет или поделиться некоторым кодом для тестирования базы данных ?!

Cheeerrrrsss !! Джорджио

Solutions Collecting From Web of "Тестирование базы данных на Android: ProviderTestCase2 или RenamingDelegatingContext?"

И ProviderTestCase и RenamingDelegatingContext уничтожат базу данных, если она уже существует, прежде чем открывать ее в ее контексте, поэтому в этом смысле они оба имеют одинаковый подход низкого уровня к открытию базы данных SQLite.

Вы используете это, открыв базу данных в своем устройстве в setUp() , после чего убедитесь, что вы работаете с новой базой данных перед каждым тестовым случаем.

Я бы предположил, что вы идете писать контент-провайдеров, а не создавать адаптеры баз данных. Вы можете использовать общий интерфейс для доступа к данным, будь то в БД или где-то по сети, дизайн контент-провайдеров может быть адаптирован для доступа к таким данным за счет незначительных издержек IPC, которые связаны с тем, что большинство из нас не должно " T должны заботиться о.

Если вы сделали это для доступа к базе данных SQLite, инфраструктура полностью управляет соединением с базой данных для вас в отдельном процессе. В качестве добавленной говядины ProviderTestCase2<ContentProvider> полностью загружает тестовый контекст для вашего поставщика контента без необходимости писать одну строку кода.

Но, это не сказано, что это не такое огромное усилие, чтобы сделать самозагрузку самостоятельно. Предположим, что у вас был адаптер базы данных; Мы просто сосредоточимся на open() для получения доступа к записи в нашу базу данных, ничего фантастического:

 public class MyAdapter { private static final String DATABASE_NAME = "my.db"; private static final String DATABASE_TABLE = "table"; private static final int DATABASE_VERSION = 1; /** * Database queries */ private static final String DATABASE_CREATE_STATEMENT = "some awesome create statement"; private final Context mCtx; private SQLiteDatabase mDb; private DatabaseHelper mDbHelper; private static class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DATABASE_CREATE_STATEMENT); } @Override public void onUpgrade(SQLiteDatabase db, int a, int b) { // here to enable this code to compile } } /** * Constructor - takes the provided context to allow for the database to be * opened/created. * * @param context the Context within which to work. */ public MyAdapter(Context context) { mCtx = context; } /** * Open the last.fm database. If it cannot be opened, try to create a new * instance of the database. If it cannot be created, throw an exception to * signal the failure. * * @return this (self reference, allowing this to be chained in an * initialization call) * @throws SQLException if the database could be neither opened or created */ public MyAdapter open() throws SQLException { mDbHelper = new DatabaseHelper(mCtx); mDb = mDbHelper.getWritableDatabase(); return this; } public void close() { mDbHelper.close(); } } 

Тогда вы можете написать свой тест как таковой:

 public final class MyAdapterTests extends AndroidTestCase { private static final String TEST_FILE_PREFIX = "test_"; private MyAdapter mMyAdapter; @Override protected void setUp() throws Exception { super.setUp(); RenamingDelegatingContext context = new RenamingDelegatingContext(getContext(), TEST_FILE_PREFIX); mMyAdapter = new MyAdapter(context); mMyAdapter.open(); } @Override protected void tearDown() throws Exception { super.tearDown(); mMyAdapter.close(); mMyAdapter = null; } public void testPreConditions() { assertNotNull(mMyAdapter); } } 

Итак, что происходит здесь, заключается в том, что контекстная реализация MyAdapter(context).open() , после MyAdapter(context).open() , всегда будет воссоздавать базу данных. Каждый тест, который вы сейчас пишете, будет противоречить состоянию базы данных после MyAdapter.DATABASE_CREATE_STATEMENT .

Я фактически использую базу данных с SQLiteOpenHelper, и у меня есть трюк для тестирования. Идея состоит в том, чтобы использовать стандартную встроенную БД во время обычного использования приложения и БД в памяти во время тестов. Таким образом, вы можете использовать четкую БД для каждого теста без данных вставки / удаления / обновления в стандартном БД. Он отлично работает для меня.

Имейте в виду, что вы можете использовать базу данных в памяти, просто передавая null в качестве имени файла базы данных. Это четко описано в документации API.

Преимущества использования встроенной БД во время тестов объясняются здесь: https://attakornw.wordpress.com/2012/02/25/using-in-memory-sqlite-database-in-android-tests/

В моем проекте у меня есть класс DBHelper, который расширяет SQLiteHelper. Как вы можете видеть, существуют стандартные методы. Я просто добавил конструктор с двумя параметрами. Разница в том, что когда я вызываю супер-конструктор, я передаю null как имя БД.

 public class DBHelper extends SQLiteOpenHelper { public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "mydatabase.db"; public DBHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public DBHelper(Context context, boolean testMode) { super(context, null, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { //create statements } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //on upgrade policy } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { //on downgrade policy } } 

Каждая «модель» в проекте расширяет DBModel, который является абстрактным классом.

 public abstract class DBModel { protected DBHelper dbhelper; public DBModel(Context context) { dbhelper = new DBHelper(context); } //other declarations and utility function omitted } 

Как обсуждалось здесь: Как я могу узнать, работает ли код внутри теста JUnit или нет? Есть способ установить, если вы используете тесты JUnit, просто выполняете поиск в элементах трассировки стека. В качестве консенсуса я модифицировал конструктор DBModel

 public abstract class DBModel { protected DBHelper dbhelper; public DBModel(Context context) { if(isJUnitTest()) { dbhelper = new DBHelper(context, true); } else { dbhelper = new DBHelper(context); } } private boolean isJUnitTest() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); List<StackTraceElement> list = Arrays.asList(stackTrace); for (StackTraceElement element : list) { if (element.getClassName().startsWith("junit.")) { return true; } } return false; } //other declarations and utility function omitted } 

Обратите внимание, что

 startsWith("junit.") 

может быть

 startsWith("org.junit.") 

в твоем случае.

У меня есть приложение, которое использует ContentProvider, поддерживаемый базой данных sqlite для предоставления данных в приложение.

Пусть PodcastDataProvider будет фактическим датаприводителем, используемым приложением.

Затем вы можете настроить поставщика тестов следующим образом:

 public abstract class AbstractPodcastDataProvider extends ProviderTestCase2<PodcastDataProvider>{ public AbstractPodcastDataProvider(){ this(PodcastDataProvider.class, Feed.BASE_AUTH); } public AbstractPodcastDataProvider(Class<PodcastDataProvider> providerClass, String providerAuthority) { super(providerClass, providerAuthority); } public void setUp() throws Exception{ super.setUp(); //clear out all the old data. PodcastDataProvider dataProvider = (PodcastDataProvider)getMockContentResolver() .acquireContentProviderClient(Feed.BASE_AUTH) .getLocalContentProvider(); dataProvider.deleteAll(); } } 

Для установки поставщика тестовых данных, который будет поддерживаться другой базой данных, чем фактическое приложение.

Чтобы протестировать DAO, создайте еще один класс, который расширяет AbstractPodcastDataProvider и использует

 getMockContentResolver(); 

Чтобы получить экземпляр распознавателя контента, который будет использовать тестовую базу данных вместо базы данных приложения.

 private static String db_path = "/data/data/android.testdb/mydb"; private SQLiteDatabase sqliteDatabase = null; private Cursor cursor = null; private String[] fields; /* * (non-Javadoc) * * @see dinota.data.sqlite.IDataContext#getSQLiteDatabase() */ public SQLiteDatabase getSQLiteDatabase() { try { sqliteDatabase = SQLiteDatabase.openDatabase(db_path, null, SQLiteDatabase.OPEN_READWRITE); sqliteDatabase.setVersion(1); sqliteDatabase.setLocale(Locale.getDefault()); sqliteDatabase.setLockingEnabled(true); return sqliteDatabase; } catch (Exception e) { return null; } } 

Если вы дадите точное местоположение sqlite db (в моем случае это db_path), используя описанный выше метод, вы можете узнать, возвращает ли он базу данных sqlited или нет.

Возможным решением может быть открытие базы данных с использованием этого метода

 myDataBase = SQLiteDatabase.openDatabase(DATABASE_NAME, null, SQLiteDatabase.OPEN_READWRITE); 

И измените имя базы данных в своих тестах. Здесь вы можете найти информацию об этом методе.