测试Android Content Provider的奇怪行为

时间:2016-04-24 19:39:45

标签: java android sqlite

我编写了一个ContentProvider来处理本地sqlite数据库上的query/insert/update/delete

出于测试目的,我写了几个测试:

TestMovieContract.java : for testing MovieContract class
TestMovieDBHelper.java : for testing MovieDBHelper class
TestUriMatcher.java    : for testing UriMatcher inside MovieContentProvider class
TestProvider.java      : for testing MovieContentProvider class

当我分别运行这些测试中的每一个时,它们都会通过但是当我运行所有这些测试时,其中一个测试失败了。它失败的断言声明是:

returnedUri = mContext.getContentResolver().insert(
         movieInsertUri,
         sampleMovieValues
  );

assertNotNull("the values have not been inserted into " + MovieContract.MovieEntry.TABLE_NAME, returnedUri);

当我检查logcat时,我在执行此特定测试时看到了异常:

04-24 19:16:00.086 6938-6963/com.example.android.popularmovies.app I/TestRunner: started: testAndroidTestCaseSetupProperly(com.example.android.popularmovies.app.data.TestProvider)
04-24 19:16:00.104 6938-6963/com.example.android.popularmovies.app I/TestRunner: finished: testAndroidTestCaseSetupProperly(com.example.android.popularmovies.app.data.TestProvider)
04-24 19:16:00.104 6938-6963/com.example.android.popularmovies.app I/TestRunner: passed: testAndroidTestCaseSetupProperly(com.example.android.popularmovies.app.data.TestProvider)
04-24 19:16:00.104 6938-6963/com.example.android.popularmovies.app I/TestRunner: started: testDelete(com.example.android.popularmovies.app.data.TestProvider)
04-24 19:16:00.122 6938-6963/? E/SQLiteLog: (1032) statement aborts at 33: [INSERT INTO movie(popularity,vote_average,original_title,backdrop_url,movie_id,original_lang,image_url,overview,title,is_adult,release_date,vote_count,is_favorite,genre_ids) VALUES (?,
04-24 19:16:00.122 6938-6963/? E/SQLiteDatabase: Error inserting popularity=8.3 vote_average=5.2 original_title=Some Android backdrop_url=http://mybackimage.com/44.png movie_id=40443 original_lang=en image_url=http://someimage.com/33.jpg overview=This is a sample overview title=Very First Android is_adult=true release_date=2015-05-23 vote_count=33345 is_favorite=true genre_ids=23,1,5
                                                 android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)
                                                     at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
                                                     at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:780)
                                                     at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
                                                     at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
                                                     at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1471)
                                                     at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1341)
                                                     at com.example.android.popularmovies.app.data.MovieContentProvider.insert(MovieContentProvider.java:197)
                                                     at android.content.ContentProvider$Transport.insert(ContentProvider.java:263)
                                                     at android.content.ContentResolver.insert(ContentResolver.java:1231)
                                                     at com.example.android.popularmovies.app.data.TestProvider.testDelete(TestProvider.java:308)
                                                     at java.lang.reflect.Method.invoke(Native Method)
                                                     at junit.framework.TestCase.runTest(TestCase.java:168)
                                                     at junit.framework.TestCase.runBare(TestCase.java:134)
                                                     at junit.framework.TestResult$1.protect(TestResult.java:115)
                                                     at junit.framework.TestResult.runProtected(TestResult.java:133)
                                                     at junit.framework.TestResult.run(TestResult.java:118)
                                                     at junit.framework.TestCase.run(TestCase.java:124)
                                                     at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
                                                     at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
                                                     at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
                                                     at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)
04-24 19:16:00.122 6938-6963/? W/SQLiteLog: (28) file unlinked while open: /data/user/0/com.example.android.popularmovies.app/databases/movie.db

此日志末尾有一个警告,我认为这很重要。它显示数据库文件已取消链接。我检查了所有代码,看看是否正确关闭了任何数据库连接。

我的问题是为什么测试在我运行TestProvider.java时通过但在我运行所有测试时失败?我该如何解决呢?

更新

我对断言失败的区域进行了一些小改动并克隆了insert语句。新代码如下:

returnedUri = mContext.getContentResolver().insert(
         movieInsertUri,
         sampleMovieValues
  );

returnedUri = mContext.getContentResolver().insert(
         movieInsertUri,
         sampleMovieValues
  );

assertNotNull("the values have not been inserted into " +  MovieContract.MovieEntry.TABLE_NAME, returnedUri);

令人惊讶的是,所有测试都通过了,并让我认为引擎盖下可能存在问题。我很感激,如果有人可以帮我解决这个问题

2 个答案:

答案 0 :(得分:1)

实现shutdown()确实可以解决Maxim G提到的问题,但是您需要确保您的代码在每次测试中都会调用它。棘手的部分是通过ContentResolver访问ContentProvider。

        mContext.getContentResolver()
          .acquireContentProviderClient(uri)
            .getLocalContentProvider()
            .shutdown();

只要您的内容提供商不在外部,就可以使用。

答案 1 :(得分:0)

问题可以与ContentProvider的多个实例一起使用ContentProvider.shutdown();

“但是,当您在ContentProvider上调用测试方法时,ContentProvider实例会在测试完成后启动并继续运行,即使后续测试实例化另一个ContentProvider。也会发生冲突,因为这两个实例通常是针对相同的基础数据源(例如,sqlite数据库)“

检查docs

示例:

@Override
@TargetApi(11)
public void shutdown() {
    mOpenHelper.close();
    super.shutdown();
}