2017-09-20 3 views
0

Als meine DB Prüfung erhalte ich folgende Fehler:Fehler beim Testen Datenbankrepository - SQLBrite, SQLDelight

  • SQLiteDiskIOException: Festplatten-E/A-Fehler (HTC Desire 620)
  • SQLiteReadOnlyDatabaseException: Versuch, eine Lese zu schreiben -only Datenbank (Moto g2)

Abhängig anscheinend auf dem Gerät teste ich es an. Der Fehler tritt nicht auf, wenn ich die App starte. Trotzdem, wenn ich die App nicht testen kann, ist wahrscheinlich etwas mit meinem Code nicht in Ordnung.

Die App verwendet zwei Bibliotheken, die SQLDelight und SQLBrite gut zusammenpassen sollen, was diese Frage etwas spezifisch machen könnte.

Für ein besseres Verständnis der Vorgänge werde ich eine kurze Beschreibung der Dateien in meinem Datenpaket geben.

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

Die Dateien Location und Run sind Reihe Modelle von SQLDelight erzeugt. LocationManager und RunManager aktivieren Sie das Generieren von sqlStatements zum Einfügen oder Entfernen von Daten aus der entsprechenden Tabelle. Unter der RunManager sieht die LocationMangager ähnlich aus.

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); 
    } 

} 

Der MyDBHelper erweitert den SQLiteOpenHelper auf eine standardmäßige Weise.

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) 
    } 
} 

Das Datenrepository fasst die verschiedenen Einfüge- und Abfrageoperationen zusammen.

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 

} 

Nun zum Hauptteil der Frage. Für den Moment habe ich die folgenden Testfälle geschrieben und führe die oben aufgelisteten Fehler durch. Interessanterweise, wenn ich die Tests getrennt durchführe, bekomme ich keine Fehler, alle Tests bestanden.

@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()); 
    } 


} 

Ich gehe davon aus, das hat etwas mit dem Zugriff auf die DB von verschiedenen Threads zu tun, aber ich weiß nicht, wie das Problem zu lösen.

Antwort

1

Ihr Problem ist, dass, wenn setUp zum zweiten Mal ausgeführt wird, DataRepository.getInstance das Repository alten Daten zurückgibt, dh es kein neues SQLiteOpenHelper schafft. Wenn Sie die Datenbank löschen, müssen Sie auch Ihre Singletons für DataRepository und MyDbHelper bereinigen.

nicht Alternativ Singletons verwenden überhaupt:

@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); 
} 
Verwandte Themen