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

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

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

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

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

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

Cheeerrrrsss!! Giorgio

4b9b3361

Ответ 1

Оба 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.

Ответ 2

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

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

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

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

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.")

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

Ответ 3

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

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

Ответ 4

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 или нет.

Ответ 5

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

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

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