2010-06-22 28 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