简体   繁体   中英

How use Roboelectric and Fest to test an android app with native binaries?

I'm trying to setup roboelectric and fest in my own project. However when I try to run ./gradlew clean test in the command line I get the following errors in the test report:

http://pastebin.com/5gaJgftf

My project does build the app without errors though. I only get this issue when I try to run tests, so it seems that Roboelectric is not aware is not aware of my native sqlcipher binaries and other binaries.

So I tried loading it with a shadow class for the runner that loads up the necessary binaries:

@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 custom 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;
        }
    }
}

You have issues to use sql cipher with robolectric?

My workaround is to use two different implementation of the SQLiteOpenHelper. One use sqlcipher and the another one the default database implementation. This both are behind a factory class, which create the SQLiteDatabase based on a static boolean flag, so the unscure database handling will be eliminated from progard.

The next issue is that both have different SQLiteDatabase classes. So again build a wrapper around the SQLiteDatabase which will be created with the right SQLiteDatabase from the SQLiteOpenHelper Wrapper. Take the Cipher variant as your base. you can ignore methods which exist at default SQLiteDatabase but not at the cipher variant. This wrapper class take the same static boolean flag to choose which database should be used. if make a mistake and take the wrong database then it should throw a null pointer exception ;)

in your app code you should now use only the wrapper classes.

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 short snippet from 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 robolectric test i set the USE_UNSECURE_DATABASE per reflection true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM