2017-09-20 1 views
0

내 DB를 테스트 할 때 나는 다음과 같은 오류를 얻을 :오류 때 데이터베이스 저장소를 테스트 - SQLBrite, SQLDelight

  • SQLiteDiskIOException : 디스크 I/O 오류 (HTC 디자 620)
  • SQLiteReadOnlyDatabaseException : 읽기 쓰기 시도 -only 데이터베이스 (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()); 
    } 


} 

다른 스레드에서 DB에 액세스하는 것과 관련이 있다고 가정합니다. 그러나이 문제를 해결하는 방법을 모르겠습니다.

답변

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

는 또한 모든 싱글 톤을 사용하지 말아

관련 문제