Android测试 - 从每个测试的干净数据库开始

时间:2012-03-21 07:29:09

标签: sqlite

我正在使用Android Instrumentation测试来测试我的应用程序。

所以我有一个扩展ActivityInstrumentationTestCase2的测试类,其中包含多个测试。代码如下所示:

public class ManageProjectsActivityTestextends ActivityInstrumentationTestCase2<ManageProjectsActivity> {
    public ManageProjectsActivityTest() {
        super("eu.vranckaert.worktime", ManageProjectsActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        getInstrumentation().getTargetContext().deleteDatabase(DaoConstants.DATABASE);
        super.setUp();
        solo = new Solo(getInstrumentation(), getActivity());
    }

    @Override
    protected void runTest() throws Throwable {
        super.runTest();
        getActivity().finish();
    }

    public void testDefaults() {
        // My test stuff
    }

    public void testAddProject() {
        // My test stuff
    }
}

因此,正在测试的活动有一个项目列表。 从数据库中检索项目列表。当没有可用的数据库时,创建数据库时,我会插入一个默认项目。

这意味着当测试运行时,这就是我所看到的:

  1. 数据库(如果有)将在设备上删除
  2. 启动第一个测试(因此启动了活动,创建了包含一个项目的数据库)
  3. 测试使用新创建的数据库,因此只有一个项目,在测试期间创建第二个项目
  4. 第一个测试完成,再次调用setUp()方法
  5. 现在应该存在的数据库再次被删除
  6. 启动第二个测试(因此启动了活动,创建了一个项目的数据库)
  7. 第二次测试也完成了
  8. 但这不是这个测试套件的作用...... 这是我的测试套件的结果:

    1. 数据库(如果有)将在设备上删除
    2. 启动第一个测试(因此启动了活动,创建了包含一个项目的数据库)
    3. 测试使用新创建的数据库,因此只有一个项目,在测试期间创建第二个项目
    4. 第一个测试完成,再次调用setUp()方法
    5. 现在应该存在的数据库再次被删除
    6. 它来了:第二个测试已经开始了(但是我的数据库没有再创建!!!我无法在设备上看到文件......)并且测试应该只显示一个项目开始但它确实显示了两个!!!
    7. 第二次测试也结束但失败了,因为我在开始时有两个项目......
    8. 一开始我没有覆盖runTest()方法,但我想也许我应该自己结束活动以强制重新创建,但它没有任何区别。

      所以似乎DB保存在内存中(因为当我明确删除它时,甚至没有在设备上创建新的DB文件)。甚至是活动,因为当在活动的onCreate中放置一个断点a时,我只会在那里进行两次测试。

      为了维护DB我使用ORMLite。您可以在此处查看我的助手课程:http://code.google.com/p/worktime/source/browse/trunk/android-app/src/eu/vranckaert/worktime/dao/utils/DatabaseHelper.java

      所以我的问题是如何强制测试一直使用不同的数据库......?

6 个答案:

答案 0 :(得分:5)

mDb.delete(DATABASE_TABLE_NAME, null, null); 

这确实是解决方案/待办事项......

我将setUp(..)方法中的第一行更改为:

cleanUpDatabase(tableList);

然后我添加了方法cleanUpDatabse(..)谎言:

private void cleanUpDatabase(List<String> dbTables) {
    Log.i(LOG_TAG, "Preparing to clean up database...");
    DatabaseHelper dbHelper = new DatabaseHelper(getInstrumentation().getTargetContext());
    ConnectionSource cs = dbHelper.getConnectionSource();
    SQLiteDatabase db = dbHelper.getWritableDatabase();

    Log.i(LOG_TAG, "Dropping all tables");
    for (String table : dbTables) {
        db.execSQL("DROP TABLE IF EXISTS " + table);
    }

    Log.i(LOG_TAG, "Executing the onCreate(..)");
    dbHelper.onCreate(db, cs);

    Log.i(LOG_TAG, "Verifying the data...");
    for (String table : dbTables) {
        Cursor c = db.query(table, new String[]{"id"}, null, null, null, null, null);
        int count = c.getCount();
        if (count != 1 && (table.equals("project") || table.equals("task"))) {
            dbHelper.close();
            Log.e(LOG_TAG, "We should have 1 record for table " + table + " after cleanup but we found " + count + " record(s)");
            throw new RuntimeException("Error during cleanup of DB, exactly one record should be present for table " + table + " but we found " + count + " record(s)");
        } else if (count != 0 && !(table.equals("project") || table.equals("task"))) {
            dbHelper.close();
            Log.e(LOG_TAG, "We should have 0 records for table " + table + " after cleanup but we found " + count + " record(s)");
            throw new RuntimeException("Error during cleanup of DB, no records should be present for table " + table + " but we found " + count + " record(s)");
        }
    }

    Log.i(LOG_TAG, "The database has been cleaned!");
    dbHelper.close();
}

这段代码在每次测试之前执行,这使我的所有测试都相互独立。

警告:为了检索对DatabaseHelper的引用(您自己的实现当然;)),您无法调用getActivity(),因为这将启动您的活动(从而完成所有初始数据库加载(如果有的话......)

答案 1 :(得分:5)

与这个问题有点相关,但是当我寻求帮助时,我降落在这里。可能对一些人有所帮助。如果使用RenamingDelegatingContext初始化数据库,它将在运行之间清除数据库。

public class DataManagerTest extends InstrumentationTestCase {

  private DataManager subject;

  @Before
  public void setUp() {
    super.setUp();

    RenamingDelegatingContext newContext = new RenamingDelegatingContext(getInstrumentation().getContext(), "test_");
    subject = new DataManager(newContext);
  }

  // tests...
}

以及相关的DataManagerClass。

public class DataManager {

    private SQLiteDatabase mDatabase;
    private SQLiteOpenHelper mHelper;
    private final String mDatabaseName = "table";
    private final int mDatabaseVersion = 1;

    protected DataManager(Context context) {
    this.mContext = context;
    createHelper();
  }

  private void createHelper() {
    mHelper = new SQLiteOpenHelper(mContext, mDatabaseName, null, mDatabaseVersion) {
      @Override
      public void onCreate(SQLiteDatabase sqLiteDatabase) {
        // createTable...
      }

      @Override
      public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        // upgrade table
      }
    };
  }
    ...
}

答案 2 :(得分:4)

InstrumentationRegistry→getContext→deleteDatabase

{% if not purpose_is_not_exist %} getTargetContext ,也许违反直觉, getContext 应该可以解决问题:

<小时/>

概要

使用getContext删除数据库 ( getTargetContext)。

android.support.test.InstrumentationRegistry

<小时/>

实施例

getContext().deleteDatabase(DbHelper.DATABASE_NAME);

答案 3 :(得分:2)

您尝试开始所有表格数据删除更改本机解决方案。

mDb.delete(DATABASE_TABLE_NAME, null, null); 

答案 4 :(得分:1)

由于我的数据库已加密,因此我需要在每个测试用例后删除实际的db文件。我无法从deleteDatabase发出RenamingDelegatingContext,因为RenamingDelegatingContext由于某种原因无法找到以前创建的数据库。

AndroidTestCase子类的解决方案:

@Override
protected void setUp() throws Exception {
    super.setUp();
    mContext = new RenamingDelegatingContext(getContext(), TEST_DB_PREFIX);

   // create db here
}

@Override
protected void tearDown() throws Exception {
    // Making RenamingDelegatingContext find the test database
    ((RenamingDelegatingContext) mContext).makeExistingFilesAndDbsAccessible();
     mContext.deleteDatabase(DB_NAME);

    super.tearDown();
}

答案 5 :(得分:0)

在每次测试之前运行它

val context: Context = ApplicationProvider.getApplicationContext()
context.deleteDatabase("myDatabaseName.db")

这也可以做成规则