测试数据库存储库时的错误-sqlbrite,sqldelight



测试我的DB时,我会收到以下错误:

  • sqlitediskioException:磁盘I/O错误(HTC Desire 620)
  • sqlitereadonlydatabaseexception:尝试编写只读数据库(Moto G2)

显然取决于我对其进行测试的设备。我运行应用程序时不会发生错误。不过,如果我无法测试该应用程序,我的代码可能有问题。

该应用程序使用两个应该很好地融合在一起的库和sqlbrite,这可能会使这个问题有些具体。

为了更好地了解正在发生的事情,我将简要描述我的数据包中的文件。

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

文件LocationRun是由SQLDELIGHT生成的行模型。LocationManagerRunManager启用生成SQLStatement以插入或从相应表中删除数据。在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());
    }

}

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

您的问题是,当setUp第二次运行时,DataRepository.getInstance返回 old> old 数据存储库,这意味着它不会创建新的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);
}

相关内容

  • 没有找到相关文章

最新更新