Ошибка SQLite при тестировании модуля базы данных с использованием robolectric - PullRequest
0 голосов
/ 30 сентября 2018

В моем приложении есть база данных SQLite, которую я поместил в папку активов.Я успешно использую его в своем приложении, но теперь я хочу протестировать методы в классе «провайдера» базы данных, в котором есть несколько методов для операций CRUD.Я пытался выполнить этот урок https://medium.com/@elye.project/android-sqlite-database-unit-testing-is-easy-a09994701162, но мой тестовый класс не прошел, за исключением

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)

Он не прошел в методе

 public void openDataBase() throws SQLException {

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

}

в соответствии с openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

Весь мой класс SQLiteOpenHelper

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

}

}

Мой класс тестирования

@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 находится на Java, а класс тестирования находится в Kotlin.Я думаю, это происходит потому, что я не делаю БД на ходу, а использую уже существующую.Я могу написать дополнительную логику для целей тестирования, но, как я понял, будет неправильно менять логику приложения только для тестирования.Итак, мой вопрос, как проверить БД, которая не создается, а просто открывается из папки ресурсов Android. \?

1 Ответ

0 голосов
/ 01 октября 2018

Счастливый, чтобы выпить весь файл, проект большой, поэтому нужно поместить его на ОДИН Диск или Google Диск. Это приложение создает базы данных SQLite на 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);
}

}

...