Java Android内容提供商奇怪行为测试

Java Android内容提供商奇怪行为测试,java,android,sqlite,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

我编写了一个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);

令人惊讶的是,所有的测试都通过了,这让我觉得引擎盖下面可能有什么问题。如果有人能帮我解决这个问题,我将不胜感激。如果有多个ContentProvider实例,请使用
ContentProvider.shutdown()

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

检查

例如:

@Override
@TargetApi(11)
public void shutdown() {
    mOpenHelper.close();
    super.shutdown();
}
实现shutdown()确实解决了Maxim G提到的问题,但您需要确保代码在每个测试中都调用它。棘手的部分是通过ContentResolver访问ContentProvider

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

只要您的内容提供程序不是外部的,这就可以工作。

我在我的内容提供程序类中有shutdown override,我应该显式调用它吗?我已经实现了shutdown方法,但在测试中,我没有访问内容提供程序实例的权限来显式调用shutdown()在tearDown()中添加调用方法。我没有访问内容提供程序对象的权限,因此无法调用itIndeed,我从中找到了测试。他们使用模拟,但并没有直接调用关机。你们使用模拟器还是真实设备?它生根了吗?它有多少内存?@MaximG模拟1G内存