SQLite existing database unit testing error using robolectric

爷,独闯天下 提交于 2019-12-13 20:27:05

问题


In my app, I have the SQLite database which I have put into assets folder. I successfully use it in my app, but now I want to test methods in database "provider" class which has several methods for CRUD operations. I tried to follow up this tutorial https://medium.com/@elye.project/android-sqlite-database-unit-testing-is-easy-a09994701162 but my test class fails with the exception

android.database.sqlite.SQLiteException: Cannot open SQLite connection, base error code: 14

at org.robolectric.shadows.ShadowSQLiteConnection.rethrow(ShadowSQLiteConnection.java:56)
at org.robolectric.shadows.ShadowSQLiteConnection.access$500(ShadowSQLiteConnection.java:33)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections.execute(ShadowSQLiteConnection.java:466)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections.open(ShadowSQLiteConnection.java:353)
at org.robolectric.shadows.ShadowSQLiteConnection.nativeOpen(ShadowSQLiteConnection.java:61)
at android.database.sqlite.SQLiteConnection.nativeOpen(SQLiteConnection.java)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
at com.education.frogtravel.ege4task.database.DBHelper.openDataBase(DBHelper.java:78)
at com.education.frogtravel.ege4task.database.DBProvider.<init>(DBProvider.kt:22)
at com.education.frogtravel.ege4task.database.DBProviderTest.setup(DBProviderTest.kt:23)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)

Caused by: com.almworks.sqlite4java.SQLiteException: [14] unable to open database file
at com.almworks.sqlite4java.SQLiteConnection.open0(SQLiteConnection.java:1353)
at com.almworks.sqlite4java.SQLiteConnection.open(SQLiteConnection.java:258)
at com.almworks.sqlite4java.SQLiteConnection.open(SQLiteConnection.java:269)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$1.call(ShadowSQLiteConnection.java:360)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$1.call(ShadowSQLiteConnection.java:353)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:452)
at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:446)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

It fails in the method

 public void openDataBase() throws SQLException {

    //Open the database
    String myPath = DB_PATH + DBScheme.DB_NAME;
    myDatabase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

}

in line with openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

My whole SQLiteOpenHelper class

import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class DBHelper extends SQLiteOpenHelper {
    private static String DB_PATH = "/data/data/my.package.path/databases/";
    private SQLiteDatabase myDatabase;
private final Context context;


public DBHelper(Context context) {
    super(context, DBScheme.DB_NAME, null, 1);
    this.context = context;
}

public void createDatabase() throws IOException{
    boolean dbExist = checkDataBase();

    if(!dbExist){//If database doesn't exist
        this.getReadableDatabase();

        try{
            copyDatabase();
        }catch(IOException e){
            throw new Error("Error copying database");
        }
    }
}


private boolean checkDataBase() {
    SQLiteDatabase checkDB = null;

    try{
        String myPath = DB_PATH + DBScheme.DB_NAME;
        checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
    }catch (SQLiteException e){
        //Database doesn't exist
    }

    return checkDB != null;
}

private void copyDatabase() throws IOException{
    InputStream inputStream = context.getAssets().open(DBScheme.DB_NAME);
    String outFileName = DB_PATH + DBScheme.DB_NAME;
    OutputStream outputStream = new FileOutputStream(outFileName);

    byte[] buffer = new byte[1024];
    int length;
    while((length = inputStream.read(buffer)) > 0){
        outputStream.write(buffer, 0, length);
    }

    outputStream.flush();
    outputStream.close();
    inputStream.close();
}

public void openDataBase() throws SQLException {

    //Open the database
    String myPath = DB_PATH + DBScheme.DB_NAME;
    myDatabase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

}

@Override
public synchronized void close() {

    if(myDatabase != null)
        myDatabase.close();

    super.close();

}


@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {

}

@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

}

}

My testing class

@RunWith(RobolectricGradleTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(LOLLIPOP), packageName = "my.pachage.path.database")

class DBProviderTest {
    lateinit var dbHelper: DBProvider

    @Before
    fun setup() {
        dbHelper = DBProvider(RuntimeEnvironment.application)
        dbHelper.clearStatistics()
    }

//commented to test because crashed in this line before
//    @After
//    fun tearDown(){
//        dbHelper.clearStatistics()
//    }

    @Test
    fun testDBUpdate(){
        dbHelper = DBProvider(RuntimeEnvironment.application)

        val wordId = 1
        val isRight = true

        dbHelper.updateWordStatistics(wordId, isRight)

        assertEquals(dbHelper.getStatisticsForWord(wordId), Statistics(wordId, 1, 1))
    }
}

DBHelper is in Java and testing class is in Kotlin. I guess this happens because I don't make the DB on the go, but use already existing one. I can write additional logic for testing purposes, but as I understood it will be wrong, to change the logic of application just for testing. So my question is how to test DB which is not created but simply opened from android assets folder.\?


回答1:


Happy to sip the whole file up project is big so need to put it on ONE Drive or Google Drive This app creates SQLite Databases on the FLY it also creates the tables on the fly

public class ManageTables extends AppCompatActivity {

Button btnMakeTable;
Button btnAddTableData;
Button btnToDetails;
Button btnDelete;
EditText etQuizTable;
EditText etTableDes;

public static String strIDT;
public static String NEW_TABLE ;

DBHelper dbHelper = new DBHelper(this);
public SQLiteDatabase db;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_manage_tables);

    setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_PORTRAIT );
    this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

    btnMakeTable = findViewById(R.id.btnMakeTable);
    btnAddTableData = findViewById(R.id.btnAddTableData);
    btnToDetails = findViewById(R.id.btnToDetails);
    btnDelete = findViewById(R.id.btnDelete);
    etQuizTable = findViewById(R.id.etQuizTable);
    etTableDes = findViewById(R.id.etTableDes);

    // Brig values over from ManageTablesListView
    etQuizTable.setText(MT_QUIZ_TABLE);
    etTableDes.setText(MT_QUIZ_NAME);

}// END onCreate

public void makeTABLE(View view){
    if(etQuizTable.getText().length() < 5 || etQuizTable.getText().length() >14){
        Toast.makeText(getApplicationContext(), "Quiz Name Max Length is 14 Characters\n"
                +"\nQuiz Name Min Length is 5 Characters", Toast.LENGTH_LONG ).show();
        return;
    }

    String tstr = "^(?!.*\\s)^(?!.*\\W)^(?!.*\\d)([a-zA-Z])";
    String astr = etQuizTable.getText().toString().trim();
    Pattern regex = Pattern.compile(tstr);
    Matcher regexMatcher = regex.matcher(astr);

    boolean foundMatch = regexMatcher.find();

    if(foundMatch == false){

        Toast.makeText( getApplicationContext(),"Upper & Lower Case Letters ONLY\n"
                + "\nNO - Numbers in Quiz Name\n"
                + "\nNO - Special Character in Quiz Name\n"
                +"\nNO - Spaces in the Quiz Name", Toast.LENGTH_LONG ).show();
        etQuizTable.requestFocus();

        return ;
    }

    // Make Table Button
    if(etQuizTable.getText().toString().isEmpty()){
        Toast.makeText(getApplicationContext(), "Enter Quiz Name", Toast.LENGTH_LONG ).show();
        etQuizTable.requestFocus();
        return;
    }
    if(etTableDes.getText().toString().isEmpty()){
        Toast.makeText(getApplicationContext(), "Enter Quiz Description", Toast.LENGTH_LONG ).show();
        etTableDes.requestFocus();
        return;
    }

    if(etTableDes.getText().length() < 5 || etTableDes.getText().length() >26){
        Toast.makeText(getApplicationContext(), "Description Max Length is 26 Characters\n"
                +"\nDescription Min Length is 5 Characters", Toast.LENGTH_LONG ).show();
        return;
    }

    NEW_TABLE = etQuizTable.getText().toString().trim();

    db = dbHelper.getWritableDatabase();
    ArrayList<String> arrTblNames = new ArrayList<>();
    Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);

    if (c.moveToFirst()) {
        while ( !c.isAfterLast() ) {
            arrTblNames.add( c.getString( c.getColumnIndex("name")) );
            c.moveToNext();
        }
    }
    c.close();
    db.close();

    for(int i=0;i<arrTblNames.size();i++) {
        if(arrTblNames.get(i).equals(NEW_TABLE)) {
            Toast.makeText(getApplicationContext(), "That Quiz Exists\n\n"
                    +"Choose a New Quiz Name", Toast.LENGTH_LONG ).show();
            etQuizTable.requestFocus();
            return;
        }
    }

    String tablename = NEW_TABLE;
    String tabledes = etTableDes.getText().toString().trim();
    dbHelper.insertIntoTABLE_TRACKER(tablename,tabledes);

    // Create NEW_TABLE and show Toast Message
    // First check for duplicate NEW_TABLE name ABOVE
    NEW_TABLE = etQuizTable.getText().toString().trim();

    dbHelper.onCreateNewTable();
    Toast.makeText(getApplicationContext(), "Quiz Created NOW\n\n"
            +"Add the First Question", Toast.LENGTH_LONG ).show();
    Intent intent = new Intent(ManageTables.this, TableCreate.class );
    startActivity( intent );
}

public void addTABLEDATA(View view){

    chkENTRY();

    // add NEW_TABLE data (records) questions and answers
    if(etQuizTable.getText().toString().equals("")){
        Toast.makeText(getApplicationContext(), "Enter Quiz Name\n\n"
                +"           OR"+"\n\nCreate Quiz First", Toast.LENGTH_LONG ).show();
        etQuizTable.requestFocus();
        return;
    }

    NEW_TABLE = etQuizTable.getText().toString().trim();

    db = dbHelper.getWritableDatabase();
    ArrayList<String> arrTblNames = new ArrayList<>();
    Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);

    if (c.moveToFirst()) {
        while ( !c.isAfterLast() ) {
            arrTblNames.add( c.getString( c.getColumnIndex("name")) );
            c.moveToNext();
        }
    }
    c.close();
    db.close();


    boolean matchFound = false;
    for(int i=0;i<arrTblNames.size();i++) {
        if(arrTblNames.get(i).equals(NEW_TABLE)) {
            Intent intent = new Intent(ManageTables.this, TableCreate.class );
            startActivity( intent );
            matchFound = true;
        }
    }
    if (!matchFound) {
        Toast.makeText(getApplicationContext(), "No Such Quiz\n\n"
                +"           OR"+"\n\nCreate Quiz First", Toast.LENGTH_LONG ).show();
        etQuizTable.requestFocus();
    }
}

public void toDetails(View view){

    chkENTRY();

    // show detail view
    if(etQuizTable.getText().toString().equals("")) {
        Toast.makeText(getApplicationContext(), "Enter Quiz Name\n\n"
                + "           OR" + "\n\nCreate Quiz First", Toast.LENGTH_LONG).show();
        etQuizTable.requestFocus();
        return;
    }

    NEW_TABLE = etQuizTable.getText().toString().trim();

    db = dbHelper.getWritableDatabase();
    ArrayList<String> arrTblNames = new ArrayList<>();
    Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);

    if (c.moveToFirst()) {
        while ( !c.isAfterLast() ) {
            arrTblNames.add( c.getString( c.getColumnIndex("name")) );
            c.moveToNext();
        }
        c.close();
        db.close();
    }

    boolean matchFound = false;
    for(int i=0;i<arrTblNames.size();i++) {

        if (arrTblNames.get(i).equals(NEW_TABLE)) {
            Intent intent = new Intent(ManageTables.this, DetailsActivity.class );
            startActivity( intent );
            matchFound = true;
        }
    }
    if (!matchFound) {
        Toast.makeText(getApplicationContext(), "No Such Quiz\n\n"
                +"           OR"+"\n\nCreate Quiz First", Toast.LENGTH_LONG ).show();
        etQuizTable.requestFocus();
    }
}

// this is btnDelete
public void onDELETE(View view) {

    if(etQuizTable.length() == 0){
        Toast.makeText(getApplicationContext(), "Enter Quiz Name", Toast.LENGTH_SHORT).show();
        return;
    }
    callDIALOG();
}

private void callDIALOG(){

    final Dialog openDialog = new Dialog(this);
    openDialog.setContentView(R.layout.delete_dialog);
    TextView tvDDT = openDialog.findViewById(R.id.tvDDT);
    tvDDT.setText("Your DELETING "+etQuizTable.getText().toString().trim());
    Button btnYES = openDialog.findViewById(R.id.btnYES);
    Button btnNO = openDialog.findViewById(R.id.btnNO);
    openDialog.setCancelable(false);

    btnYES.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            NEW_TABLE = etQuizTable.getText().toString().trim();

            db = dbHelper.getWritableDatabase();
            ArrayList<String> arrTblNames = new ArrayList<>();
            Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);

            if (c.moveToFirst()) {
                while ( !c.isAfterLast() ) {
                    arrTblNames.add( c.getString( c.getColumnIndex("name")) );
                    c.moveToNext();
                }
                c.close();
                db.close();
            }

            boolean matchFound = false;
            for(int i=0;i<arrTblNames.size();i++) {

                if (arrTblNames.get(i).equals(NEW_TABLE)) {
                    Intent intent = new Intent(ManageTables.this, DetailsActivity.class );
                    startActivity( intent );
                    matchFound = true;
                }
            }
            if (!matchFound) {
                Toast.makeText(getApplicationContext(), "NO MATCH\n\n"
                        +"CLICK NO AND"+"\n\nCHECK QUIZ NAME", Toast.LENGTH_LONG ).show();
                etQuizTable.requestFocus();
                return;
            }

            //chkENTRY(null);
            // THIS method deletes the TABLE NAME from TABLE_TRACKER
            // THEN DROPS the corresponding CREATED TABLE from the DB
            // doDrop makes 4 calls to DBHelper
            dbHelper.deleteFROM_TABLE_RESPONSE();
            strIDT = dbHelper.getCol_IDT();
            dbHelper.deleteTABLE_FROM_TABLE_TRACKER();

            dbHelper.dropTABLE();
            Intent intent = new Intent(ManageTables.this,ManageTablesListView.class);
            startActivity(intent);

            Toast.makeText(getApplicationContext(), "Quiz Data Deleted ", Toast.LENGTH_SHORT).show();
            openDialog.dismiss();
        }
    });
    btnNO.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            openDialog.dismiss();
        }
    });
    openDialog.show();
}

public void chkENTRY(){

    String tstr = "^(?!.*\\s)^(?!.*\\W)^(?!.*\\d)([a-zA-Z])";
    String astr = etQuizTable.getText().toString().trim();
    Pattern regex = Pattern.compile(tstr);
    Matcher regexMatcher = regex.matcher(astr);

    boolean foundMatch = regexMatcher.find();

    if(foundMatch == false){

        Toast.makeText( getApplicationContext(),"Upper & Lower Case Letters ONLY\n"
                + "\nNO - Numbers in Quiz Name\n"
                + "\nNO - Special Character in Quiz Name\n"
                +"\nNO - Spaces in the Quiz Name", Toast.LENGTH_LONG ).show();
        etQuizTable.requestFocus();

        return ;
    }
}

public void onBackPressed() {
    Intent intent = new Intent(ManageTables.this,ManageTablesListView.class);
    startActivity(intent);
}

}



来源:https://stackoverflow.com/questions/52576283/sqlite-existing-database-unit-testing-error-using-robolectric

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!