2010-06-22 1 views
29

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

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

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

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

Cheeerrrrsss !! Giorgio

+0

[Проверка базы данных андроида в стиле JUnit4] (http://www.singhajit.com/testing-android-database/) –

ответ

21

И 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); 
} 

} 

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

+0

Я начал пинать себя за то, что не начал путь ContentProvider с самого начала, но я пошел вперед и сделал один. Теперь вернемся к тестированию ... – LostNomad311

+0

Что это значит «RenamingDelegatingContext»? Почему мы это используем? Будет создана новая база данных для каждого метода? –

+0

Я нахожу, что при запуске 'AndroidTestCase' /' RenamingDelegatingContext' вместе с 'ActivityInstrumentationTestCase2', что база данных сохраняется из теста для тестирования. Запуск только «AndroidTestCase» воссоздает БД каждый раз. – Tad

-1

Возможное решение может быть, чтобы открыть базу данных с помощью этого метода

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

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

1

У меня есть приложение, которое использует 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(); 

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

0
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_PATH), с помощью описанного выше метода вы можете найти выход возвращается ли оно sqlitedatabase или нет.

6

Я фактически использую базу данных с 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 

} 

Как обсуждалось здесь: How can I find out if code is running inside a JUnit test or not? есть способ установить, если вы работаете JUnit тесты, просто поиск в стек микроэлементами. Как conseguence, я изменил 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.") 

в вашем случае.

+0

Спасибо за отличные идеи и предложения. – JulianHarty

Смежные вопросы