2010-06-22 27 views
29

我已经实现了从某些类(具有模式DAO)的android.database包中使用SQLiteOpenHelper访问数据库。在Android上测试数据库:ProviderTestCase2或RenamingDelegatingContext?

我使用AndroidTestCase为这些类写了一些junit测试,但这会导致测试使用与应用程序相同的数据库。

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

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

Cheeerrrrsss! 乔治

+0

[测试Android数据库JUnit4风格](http://www.singhajit.com/testing-android-database/) – 2015-09-28 17:21:35

回答

21

ProviderTestCaseRenamingDelegatingContext都会破坏数据库,如果数据库在打开它的上下文之前已经存在,那么从这个意义上说,它们都具有相同的打开SQLite数据库的低级方法。

您可以通过在您的夹具中打开数据库setUp()来利用这一点,这会确保您在每个测试用例之前使用新数据库。

我建议你去写内容提供者而不是创建数据库适配器。您可以使用通用接口访问数据,将数据存储在数据库或网络中的某处,可以适应内容提供商的设计以访问此类数据,但代价是IPC的开销比较大,我们大多数人不应该“不必关心。

如果你这样做了访问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 2012-01-25 03:19:40

+0

这是什么意思'RenamingDelegatingContext'?我们为什么使用这个?它会为每种方法创建新的数据库? – 2014-05-15 10:45:36

+0

我发现在运行一个'AndroidTestCase' /'RenamingDelegatingContext'和一个ActivityInstrumentationTestCase2时,数据库会从测试中持续测试。每次只运行'AndroidTestCase'都会重新创建数据库。 – Tad 2016-04-06 02:29:31

-1

一个可能的解决方案可以使用这种方法

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

打开数据库,并在测试中更改数据库的名称。 Here你可以找到关于这种方法的一些信息。

1

我有一个应用程序使用由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(); 

方法获取将使用测试数据库,而不是应用程序数据库的内容解析器的一个实例。

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 2017-01-13 21:35:31