How do I use Roboelectric and Fest to test an Android app with native binaries?

I am trying to set up roboelectric and fest in my own project. However, when I try to run. / gradlew clean test on the command line, I get the following errors in the test report:

http://pastebin.com/5gaJgftf

My project actually builds an application with no errors. I only get this problem when I try to run tests, so it seems like Roboelectric doesn't know that I don't know about my own sqlcipher binaries and other binaries.

So, I tried loading it with a runner shadow class that loads the binaries needed:

@Config(emulateSdk = 18, shadows={MyJniClass.class})
@RunWith(RobolectricTestRunner.class)
public class MainActivityBuildTest {

    @Test
    public void testSomething() throws Exception {
        Activity activity = Robolectric.buildActivity(MainActivity.class).create().get();
        assertTrue(activity != null);
    }
}

      

Using my jniloader shadow class

@Implements(RobolectricTestRunner.class)
class MyJniClass {
    static {
        try {
            System.loadLibrary("libdatabase_sqlcipher");
            System.loadLibrary("libdatabase_android");
            System.loadLibrary("libstlport_shared");
        } catch (UnsatisfiedLinkError e) {
            // only ignore exception in non-android env
            if ("Dalvik".equals(System.getProperty("java.vm.name"))) throw e;
        }
    }
}

      

+3


source to share


1 answer


Are you having trouble using sql encryption with robolectric?

My workaround is to use two different SQLiteOpenHelper implementations. One is using sqlcipher and the other is the default implementation for the database. They are both behind a factory class that create a SQLiteDatabase based on a static boolean flag, so implicit database handling will be excluded from progard.

The next problem is that both have different SQLiteDatabase classes. So create a wrapper around the SQLiteDatabase again, which will be created with the corresponding SQLiteDatabase from the SQLiteOpenHelper Wrapper. Take the Cipher variant as a base. you can ignore methods that exist by default SQLiteDatabase, but not by encryption option. This wrapper class accepts the same static boolean flag to choose which database to use. if you make a mistake and take the wrong database, then it should throw a null pointer exception;)

you should now only use wrapper classes in your application code.

example for DatabaseHelper wrapper



public class MyDatabaseHelper {

public static final String DATABASE_NAME = "my.db";
public static final int DATABASE_VERSION = 1;

MyEncryptedDatabaseHelper encryptedDatabase;
MyUnsecureDatabaseHelper unsecureDatabase;


public MyDatabaseHelper(Context context) {
    if (ReleaseControl.USE_UNSECURE_DATABASE) {
        unsecureDatabase = new MyUnsecureDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
        return;
    }
    encryptedDatabase = new MyEncryptedDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
}


public MySQLiteDatabase getWritableDatabase(String password) throws MySQLiteException {
    if (ReleaseControl.USE_UNSECURE_DATABASE) {
        try {
            return new MySQLiteDatabase(unsecureDatabase.getWritableDatabase());
        } catch (android.database.SQLException e) {
            throw new MySQLiteException(e);
        }
    }
    try {
        return new MySQLiteDatabase(encryptedDatabase.getWritableDatabase(password));
    } catch (net.sqlcipher.database.SQLiteException e) {
        throw new MySQLiteException(e);
    }
}
}

      

and a short snippet from the SQLiteDatabase wrapper

public class MySQLiteDatabase {

private net.sqlcipher.database.SQLiteDatabase encryptedDatabase;
private android.database.sqlite.SQLiteDatabase unsecureDatabase;

public MySQLiteDatabase(SQLiteDatabase database) {
    encryptedDatabase = database;
}

public MySQLiteDatabase(android.database.sqlite.SQLiteDatabase database) {
    unsecureDatabase = database;
}

public static void loadLibs(android.content.Context context) {
    if (ReleaseControl.USE_UNSECURE_DATABASE) { return; }
    SQLiteDatabase.loadLibs(context);
}

public static int releaseMemory() {
    if (ReleaseControl.USE_UNSECURE_DATABASE) {
        return android.database.sqlite.SQLiteDatabase.releaseMemory();
    }
    return net.sqlcipher.database.SQLiteDatabase.releaseMemory();
}

public static SQLiteDatabase openDatabase(String path, String password, MyCursorFactory factory, int flags) {
    if(factory == null) factory = new NullCursorFactory();
    if (ReleaseControl.USE_UNSECURE_DATABASE) {
        return new MySQLiteDatabase(android.database.sqlite.SQLiteDatabase.openDatabase(path, factory.getUnsecure(), flags));
    }
    return new MySQLiteDatabase(net.sqlcipher.database.SQLiteDatabase.openDatabase(path, password, factory.getEncrypted(), flags));
}

      

In a roboelectric test, I set USE_UNSECURE_DATABASE to reflection true

+2


source







All Articles