在Android上testing数据库:ProviderTestCase2或RenamingDelegatingContext?

我已经实现了使用SQLiteOpenHelper从一些类(模式DAO)android.database包中访问数据库

我使用AndroidTestCase为这些类编写了一些junittesting,但是这会导致testing使用与应用程序相同的数据库。

我读了ProviderTestCase2RenamingDelegatingContext可以用来单独testing数据库。 不幸的是,我找不到任何漂亮的教程/示例,显示如何使用ProviderTestCase2 / RenamingDelegatingContexttesting数据库。

任何人都可以指向我的地方或给我一些提示或共享一些代码进行数据库testing?!

Cheeerrrrsss! 乔治

Solutions Collecting From Web of "在Android上testing数据库:ProviderTestCase2或RenamingDelegatingContext?"

ProviderTestCaseRenamingDelegatingContext都会在数据库已经存在之前破坏数据库,所以在这种情况下,它们都有相同的打开SQLite数据库的低级方法。

您可以通过在setUp()打开数据库来利用这一点,这将确保您在每个testing用例之前使用全新的数据库。

我build议你去写内容提供者而不是创build数据库适配器。 您可以使用通用接口访问数据,将数据存储在数据库中或通过networking中的某个地方,可以适应内容提供商的devise以访问这些数据,但代价是一些IPC开销,不必关心。

如果你这样做是为了访问一个SQLite数据库,那么框架将在一个单独的进程中为你完全pipe理数据库连接。 由于添加了牛肉, ProviderTestCase2<ContentProvider>完全为您的内容提供程序引导一个testing上下文,而无需编写一行代码。

但是,这并不是说这不是一个巨大的努力来做自我引导。 所以假设你有一个数据库适配器, 我们只关注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(); } } 

那么你可以这样写你的testing:

 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()的上下文实现将始终重新创build数据库。 您现在编写的每个testing都将在调用MyAdapter.DATABASE_CREATE_STATEMENT之后与数据库的状态相MyAdapter.DATABASE_CREATE_STATEMENT

我实际上使用SQLiteOpenHelper数据库,我有一个testing技巧。 这个想法是在正常使用应用程序的过程中使用标准的文件存储数据库,在testing过程中使用内存数据库。 通过这种方式,您可以在每个testing中使用一个清晰​​的数据库,而无需在标准数据库中插入/删除/更新数据。 这对我来说可以。

请记住,您可以使用内存数据库,只是传递null作为数据库文件的名称。 这在API文档中有明确logging。

在testing过程中使用内存数据库的优点在这里解释: 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 } 

正如这里所讨论的: 我怎样才能知道代码是否在JUnittesting中运行? 有一种方法可以确定您是否正在运行JUnittesting,只需在堆栈跟踪元素中进行search即可。 作为一个推论,我修改了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.") 

在你的情况。

我有一个应用程序使用由sqlite数据库支持的ContentProvider向应用程序提供数据。

让PodcastDataProvider成为应用程序使用的实际数据提供者。

然后你可以设置一个testing提供者,如下所示:

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

设置一个testing数据提供程序,它将由不同于实际应用程序的数据库提供支持。

要testingDAO,创build另一个扩展AbstractPodcastDataProvider的类并使用

 getMockContentResolver(); 

方法来获取将使用testing数据库而不是应用程序数据库的内容parsing器的实例。

 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或不。

一个可能的解决scheme可以是使用这种方法打开数据库

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

并在testing中更改数据库名称。 在这里你可以find关于这个方法的一些信息。