web-dev-qa-db-ja.com

Androidでのデータベースのテスト:ProviderTestCase2またはRenamingDelegatingContext?

一部のクラス(パターンDAO)内のAndroid.databaseパッケージからSQLiteOpenHelperを使用してデータベースへのアクセスを実装しました。

AndroidTestCaseを使用してこれらのクラスのjunitテストをいくつか作成しましたが、これにより、テストはアプリケーションと同じデータベースを使用します。

私はそれを読んだProviderTestCase2またはRenamingDelegatingContextを使用して、データベースを個別にテストできます。残念ながら、ProviderTestCase2/RenamingDelegatingContextを使用してデータベースをテストする方法を示す素敵なチュートリアル/例が見つかりませんでした。

誰かが私をどこかに向けることができますかORヒントを教えてくださいORデータベーステスト用のコードを共有してください!?

Cheeerrrrsss !!ジョルジオ

29
Giorgio

ProviderTestCaseRenamingDelegatingContextはどちらも、データベースをコンテキスト内で開く前にデータベースがすでに存在する場合、データベースを破棄します。その意味で、どちらもSQLiteデータベースを開くための同じ低レベルのアプローチを採用しています。

これを活用するには、フィクスチャでsetUp()のデータベースを開きます。これにより、各テストケースの前に新しいデータベースで作業できるようになります。

データベースアダプタを作成するのではなく、コンテンツプロバイダーを作成することをお勧めします。データにアクセスするための共通のインターフェイスを使用できます。データがDBに保存されている場合でも、ネットワーク上のどこかに保存されている場合でも、コンテンツプロバイダーの設計は、IPC =私たちのほとんどが気にする必要のないオーバーヘッドが関係しています。

SQLiteデータベースにアクセスするためにこれを行った場合、フレームワークは別のプロセスでデータベース接続を完全に管理します。追加された牛肉として、_ProviderTestCase2<ContentProvider>_は、コードを1行も記述しなくても、コンテンツプロバイダーのテストコンテキストを完全にブートストラップします。

しかし、それは自分でブートストラップを行うのにそれほど大きな努力ではないとは言われていません。したがって、データベースアダプタ自体があると仮定します。データベースへの書き込みアクセスを取得するための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_が呼び出された後のデータベースの状態に対して行われます。

21

私は実際にSQLiteOpenHelperでデータベースを使用しており、テストのコツがあります。アイデアは、アプリの通常の使用中に標準のファイルに保存されたDBを使用し、テスト中にインメモリDBを使用することです。このようにして、標準DBにデータを挿入/削除/更新せずに、テストごとにクリアDBを使用できます。それは私にとってはうまくいきます。

データベースファイルの名前としてnullを渡すだけで、インメモリデータベースを使用できることに注意してください。これは、APIドキュメントに明確に文書化されています。

テスト中にインメモリDBを使用する利点は、ここで説明されています: https://attakornw.wordpress.com/2012/02/25/using-in-memory-sqlite-database-in-Android-tests/

私のプロジェクトには、SQLiteHelperを拡張するDBHelperクラスがあります。ご覧のとおり、標準的な方法があります。 2つのパラメーターを持つコンストラクターを追加しただけです。違いは、スーパーコンストラクターを呼び出すときに、DB名として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.")

あなたの場合。

6
Lipsyor

Sqliteデータベースに裏打ちされたContentProviderを使用してアプリケーションにデータを提供するアプリケーションがあります。

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();

アプリケーションデータベースの代わりにテストデータベースを使用するコンテンツリゾルバーのインスタンスを取得するメソッド。

1
bable5
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)の正確な場所を指定すると、上記のメソッドを使用して、sqliteデータベースが返されるかどうかを確認できます。

0
Rakhita