最近我在项目中启用了StrickMode
,很多测试用例都失败了。
问题在于没有关闭Cursor
。解决大多数失败并不难,但有一些我无法弄清楚。这个测试类有五个测试。由于我启用了严格模式,因此mMockContentResolver.query
的每次调用都会失败并显示上面添加的消息。在测试结束时添加cursor.close()也不会改变任何东西。所以我想知道也许我应该使用MockContentProvider
而不是MockContentResolver
。
更新:即使测试类只有测试testWithClosedCursor,它仍然在mMockContentResolver.query
MyProvider类看起来像这样,有7个表(我删除了这段代码,因为它看起来一样):
public class MyProvider extends ContentProvider {
private static UriMatcher sUriMatcher;
private static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(new ContextWrapper(context) {
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
File dir = new File(Taro.DATABASES_PATH);
if (!dir.exists()) {
dir.mkdirs();
}
String databasePath = Taro.DATABASES_PATH + File.separator + name;
return SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.CREATE_IF_NECESSARY);
}
}, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
DatabaseUpgrade.createTaskTemplatesTable(sqLiteDatabase);
}
private DatabaseHelper mDatabaseHelper;
@Override
public boolean onCreate() {
mDatabaseHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String tableName;
switch (sUriMatcher.match(uri)) {
case TASK_TEMPLATES:
qb.setProjectionMap(sTaskTemplatesProjectionMap);
tableName = TASK_TEMPLATES_TABLE_NAME;
break;
case TASK_TEMPLATE_ID:
qb.setProjectionMap(sTaskTemplatesProjectionMap);
tableName = TASK_TEMPLATES_TABLE_NAME;
break;
default:
throw new IllegalArgumentException(UNKNOWN_URI + uri);
}
qb.setTables(tableName);
SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, where, whereArgs, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case TASK_TEMPLATES:
return TaskTemplatesContract.TaskTemplateColumns.CONTENT_TYPE;
case TASK_TEMPLATE_ID:
return TaskTemplatesContract.TaskTemplateColumns.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException(UNKNOWN_URI + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
String table;
Uri contentUri;
switch (sUriMatcher.match(uri)) {
case TASK_TEMPLATES:
table = TASK_TEMPLATES_TABLE_NAME;
contentUri = TaskTemplatesContract.TaskTemplateColumns.CONTENT_URI;
break;
default:
throw new IllegalArgumentException(UNKNOWN_URI + uri);
}
ContentValues values;
if (contentValues != null) {
values = new ContentValues(contentValues);
} else {
values = new ContentValues();
}
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
long rowId = db.insert(table, null, values);
if (rowId > 0) {
Uri returnURI = ContentUris.withAppendedId(contentUri, rowId);
getContext().getContentResolver().notifyChange(returnURI, null);
return returnURI;
}
throw new SQLException("Failed to insert row into " + uri);
}
}
测试类代码
public class MyProviderTestCase extends ProviderTestCase2<MyProvider> {
private MockContentResolver mMockContentResolver;
protected void setUp() throws java.lang.Exception {
super.setUp();
mMockContentResolver = getMockContentResolver();
}
public void testSomeTest() {
final ContentValues contentValues = defaultTaskContentValues();
final Uri uri = mMockContentResolver.insert(TaskTemplatesContract.TaskTemplateColumns.CONTENT_URI, contentValues);
assertNotNull(uri);
Cursor cursor = mMockContentResolver.query(TaskTemplatesContract.TaskTemplateColumns.CONTENT_URI, null, "ID=?", new String[]{TASK_ID}, null);
assertEquals(1, cursor.getCount());
contentValues.put(TaskTemplatesContract.TaskTemplateColumns.NAME, "View contact data.");
int rowsUpdated = mMockContentResolver.update(TaskTemplatesContract.TaskTemplateColumns.CONTENT_URI, contentValues, "NAME=?", new String[]{TASK_NAME});
assertEquals(1, rowsUpdated);
cursor = mMockContentResolver.query(uri, null, null, null, null);
assertTrue(cursor.moveToNext());
}
public void testWithClosedCursor() {
final ContentValues contentValues = defaultTaskContentValues();
final Uri uri = mMockContentResolver.insert(TaskTemplatesContract.TaskTemplateColumns.CONTENT_URI, contentValues);
assertNotNull(uri);
Cursor cursor = mMockContentResolver.query(TaskTemplatesContract.TaskTemplateColumns.CONTENT_URI, null, "ID=?", new String[]{TASK_ID}, null);
assertEquals(1, cursor.getCount());
cursor.close();
}
失败消息:
Finalizing a Cursor that has not been deactivated or closed. database = /mnt/sdcard/myapp/databases/job_templates.db, table = task_templates, query = SELECT position, id, instructions, _id, oid, name, form_id, type, job_id FROM task_templates WHERE (
E/StrictMode( 493): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
E/StrictMode( 493): at android.database.sqlite.SQLiteCursor.<init>(SQLiteCursor.java:214)
E/StrictMode( 493): at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:53)
E/StrictMode( 493): at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1356)
E/StrictMode( 493): at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:330)
E/StrictMode( 493): at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:280)
E/StrictMode( 493): at org.my.app.databases.providers.MyProvider.query(MyProvider.java:244)
E/StrictMode( 493): at android.content.ContentProvider$Transport.query(ContentProvider.java:187)
E/StrictMode( 493): at android.content.ContentResolver.query(ContentResolver.java:262)
E/StrictMode( 493): at org.my.app.unit.MyProviderTestCase.testSomeTest(MyProviderTestCase.java:138)
E/StrictMode( 493): at java.lang.reflect.Method.invokeNative(Native Method)
E/StrictMode( 493): at java.lang.reflect.Method.invoke(Method.java:507)
E/StrictMode( 493): at junit.framework.TestCase.runTest(TestCase.java:154)
E/StrictMode( 493): at junit.framework.TestCase.runBare(TestCase.java:127)
E/StrictMode( 493): at junit.framework.TestResult$1.protect(TestResult.java:106)
E/StrictMode( 493): at junit.framework.TestResult.runProtected(TestResult.java:124)
E/StrictMode( 493): at junit.framework.TestResult.run(TestResult.java:109)
E/StrictMode( 493): at junit.framework.TestCase.run(TestCase.java:118)
E/StrictMode( 493): at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
E/StrictMode( 493): at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
E/StrictMode( 493): at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:529)
E/StrictMode( 493): at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1448)
W/System.err( 493): StrictMode VmPolicy violation with POLICY_DEATH; shutting down.
和StrictMode代码
private void strictMode() {
if (isDeveloperModeEnabled() && isRunningGingerbreadVersionOrHigher()) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
}
有人有过类似的问题吗? 欢呼声。