2017-09-20 66 views
0

在测试我的数据库,我得到了以下错误:错误时,测试数据库存储库 - SQLBrite,SQLDelight

  • SQLiteDiskIOException:磁盘I/O错误(HTC Desire的620)
  • SQLiteReadOnlyDatabaseException:尝试写一读 - 仅限数据库(Moto g2)

根据显然在设备上测试它。 运行应用程序时,错误不会发生。不过,如果我无法测试应用程序,那么我的代码可能有问题。

该应用程序使用两个应该一起使用SQLDelight和SQLBrite的库,这可能会使这个问题有点特定。

为了更好地理解发生了什么,我将简要描述我的数据包中的文件。

-+-data-+ 
|  |-manager-+ 
|  |   |-LocationManager 
|  |   |-RunManager 
|  |-model-+ 
|  |  |-Location 
|  |  |-Run 
|-DatabaseContract 
|-DataRepository 
|-MyDBHelper 

的文件LocationRun是由SQLDelight生成的行模式。 LocationManagerRunManager启用生成sqlStatements插入或从相应的表中删除数据。在RunManager下方的LocationMangager看起来类似。

public class RunManager { 

    public final Run.InsertRun insertRace; 
    public final Run.DeleteRun deleteRun; 
    public final Run.DeleteRunWhereTimeSmallerThan deleteRunWhereTimeSmallerThan; 

    public RunManager(SQLiteDatabase db) { 
     insertRace = new Run.InsertRun(db); 
     deleteRun = new Run.DeleteRun(db); 
     deleteRunWhereTimeSmallerThan = new Run.DeleteRunWhereTimeSmallerThan(db); 
    } 

} 

MyDBHelper以标准方式扩展SQLiteOpenHelper。

public class MyDbHelper extends SQLiteOpenHelper { 

    private static final int DATABASE_VERSION = 1; 
    public static final String DATABASE_NAME = "runner.db"; 

    private static MyDbHelper INSTANCE = null; 

    private MyDbHelper(Context context) { 
     super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    } 

    public static MyDbHelper getInstance(Context context) { 
     if (INSTANCE == null) { 
      INSTANCE = new MyDbHelper(context); 
     } 
     return INSTANCE; 
    } 

    @Override 
    public void onCreate(SQLiteDatabase sqLiteDatabase) { 
     // create table (omitted) 
    } 

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

数据存储库聚合了各种插入和查询操作。

public class DataRepository implements DatabaseContract { 

    private static DataRepository INSTANCE = null; 

    BriteDatabase briteDatabase; 
    LocationManger locationManger; 
    RunManager runManager; 

    private DataRepository(Context context) { 
     SqlBrite sqlBrite = new SqlBrite.Builder().build(); 
     MyDbHelper helper = MyDbHelper.getInstance(context); 
     briteDatabase = sqlBrite.wrapDatabaseHelper(helper, Schedulers.io()); 
     locationManger = new LocationManger(briteDatabase.getWritableDatabase()); 
     runManager = new RunManager(briteDatabase.getWritableDatabase()); 
    } 

    public static DataRepository getInstance(Context context) { 
     if (null == INSTANCE) { 
      INSTANCE = new DataRepository(context); 
     } 
     return INSTANCE; 
    } 

    @Override 
    public Observable<List<Run>> getAllRun() { 
     return briteDatabase.createQuery(
       Run.TABLE_NAME, 
       Run.SELECT_ALL 
     ).mapToList(Run.MAPPER::map); 
    } 

    @Override 
    public Observable<List<Location>> getLocationsForRun(long id) { 
     return briteDatabase.createQuery(
       Location.TABLE_NAME, Location.FACTORY.selectAllByRace(id).statement 
     ).mapToList(Location.MAPPER::map); 
    } 

    @Override 
    public long insertRun(double distance, long duration, double avgSpeed, long timestamp) { 
     runManager.insertRace.bind(distance, duration, avgSpeed, timestamp); 
     return briteDatabase.executeInsert(Run.TABLE_NAME, runManager.insertRace.program); 
    } 

    @Override 
    public void deleteRun(long id) { 
     runManager.deleteRun.bind(id); 
     briteDatabase.executeUpdateDelete(Run.TABLE_NAME, runManager.deleteRun.program); 
    } 

    @Override 
    public void deleteRunWhereTimestampSmallerThan(long timestamp) { 
     runManager.deleteRunWhereTimeSmallerThan.bind(timestamp); 
     briteDatabase.executeUpdateDelete(Run.TABLE_NAME, runManager.deleteRunWhereTimeSmallerThan.program); 
    } 

    @Override 
    public long insertLocation(long raceId, double lat, double lng, double alt, long timestamp) { 
     locationManger.insertLocation.bind(raceId, lat, lng, alt, timestamp); 
     return briteDatabase.executeInsert(Location.TABLE_NAME, locationManger.insertLocation.program); 
    } 

    public Observable<List<SingleRun>> getAllSingleRunModels() { 
     return briteDatabase.createQuery(
       Run.TABLE_NAME, 
       Run.SELECT_ALL 
     ).mapToList(Run.MAPPER::map) 
     // omitted 

} 

现在问题的主要部分。现在,我编写了以下测试用例,并运行到顶部列出的错误。 有趣的是,当我单独运行测试时,我没有收到任何错误,所有测试都通过了。

@RunWith(AndroidJUnit4.class) 
@LargeTest 
public class DataRepositoryTest { 

    private Context context; 
    private DataRepository mDataRepository; 

    @Before 
    public void setUp() { 
     context = InstrumentationRegistry.getTargetContext(); 
     context.deleteDatabase(MyDbHelper.DATABASE_NAME); 
     mDataRepository = DataRepository.getInstance(InstrumentationRegistry.getTargetContext()); 
    } 

    @Test 
    public void testPreConditions() { 
     Assert.assertNotNull(context); 
     Assert.assertNotNull(mDataRepository); 
    } 

    @Test 
    public void testInsertRace() { // this test failes when all tests are run. 
     long raceID1 = mDataRepository.insertRun(5.0, 35, 3.5, 1000); 
     Assert.assertEquals(1, raceID1); 
     long raceID2 = mDataRepository.insertRun(10.0, 70, 3.5, 2000); 
     Assert.assertEquals(2, raceID2); 
     long locationID1 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1000); 
     Assert.assertEquals(1, locationID1); 
     long locationID2 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1001); 
     Assert.assertEquals(2, locationID2); 
     long locationID3 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1002); 
     Assert.assertEquals(3, locationID3); 
     long locationID4 = mDataRepository.insertLocation(raceID1, 0.5, 0.5, 0, 1003); 
     Assert.assertEquals(4, locationID4); 
     long locationID5 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2000); 
     Assert.assertEquals(5, locationID5); 
     long locationID6 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2001); 
     Assert.assertEquals(6, locationID6); 
     long locationID7 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2002); 
     Assert.assertEquals(7, locationID7); 
     long locationID8 = mDataRepository.insertLocation(raceID2, 0.5, 0.5, 0, 2003); 
     Assert.assertEquals(8, locationID8); 
    } 

    @Test 
    public void testRaceObservable() { 
     long raceID1 = mDataRepository.insertRun(5.0, 35, 3.5, 1000); 
     Run run1 = Run.FACTORY.creator.create(raceID1, 5.0, 35l, 3.5, 1000l); 
     Assert.assertEquals(1, raceID1); 
     long raceID2 = mDataRepository.insertRun(10.0, 70, 3.5, 2000); 
     Run run2 = Run.FACTORY.creator.create(raceID2, 10.0, 70l, 3.5, 2000l); 
     Assert.assertEquals(2, raceID2); 
     List<Run> expectedResult = Arrays.asList(run1, run2); 
     Assert.assertEquals(expectedResult, mDataRepository.getAllRun().blockingFirst()); 
    } 


} 

我认为这与从不同线程访问数据库有关,但我不知道如何解决问题。

回答

1

你的问题是,当setUp运行第二次,DataRepository.getInstance返回数据存储库,这意味着它不会创建一个新SQLiteOpenHelper。当你删除数据库时,你还需要清理DataRepository和MyDbHelper的单例。

或者不使用单身可言:

@Before 
public void setUp() { 
    context = InstrumentationRegistry.getTargetContext(); 
    context.deleteDatabase(MyDbHelper.DATABASE_NAME); 
    mDataRepository = new DataRepository(InstrumentationRegistry.getTargetContext()); 
} 

// In DataRepository.java 
DataRepository(Context context) { 
    SqlBrite sqlBrite = new SqlBrite.Builder().build(); 
    MyDbHelper helper = new MyDbHelper(context); 
    briteDatabase = sqlBrite.wrapDatabaseHelper(helper, Schedulers.io()); 
    locationManger = new LocationManger(briteDatabase.getWritableDatabase()); 
    runManager = new RunManager(briteDatabase.getWritableDatabase()); 
} 

// In MyDbHelper.java 
MyDbHelper(Context context) { 
    super(context, DATABASE_NAME, null, DATABASE_VERSION); 
}