3

我正在尝试在我自己的项目中设置 roboelectric 和 fest。但是,当我尝试在命令行中运行 ./gradlew clean test 时,我在测试报告中收到以下错误:

http://pastebin.com/5gaJgftf

我的项目确实构建了没有错误的应用程序。我只在尝试运行测试时遇到这个问题,所以似乎 Roboelectric 不知道不知道我的本机 sqlcipher 二进制文件和其他二进制文件。

因此,我尝试使用加载必要二进制文件的运行程序的影子类加载它:

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

使用我的自定义 jniloader 影子类

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

1 回答 1

4

您在将 sql 密码与 robolectric 一起使用时遇到问题吗?

我的解决方法是使用 SQLiteOpenHelper 的两种不同实现。一种使用 sqlcipher,另一种使用默认数据库实现。这两者都在工厂类的后面,它基于静态布尔标志创建 SQLiteDatabase,因此不安全的数据库处理将从 progard 中消除。

下一个问题是两者都有不同的 SQLiteDatabase 类。因此,再次围绕 SQLiteDatabase 构建一个包装器,该包装器将使用 SQLiteOpenHelper Wrapper 中的正确 SQLiteDatabase 创建。以 Cipher 变体为基础。您可以忽略默认 SQLiteDatabase 中存在但密码变体中不存在的方法。这个包装类采用相同的静态布尔标志来选择应该使用哪个数据库。如果犯了错误并使用了错误的数据库,那么它应该抛出一个空指针异常;)

在您的应用程序代码中,您现在应该只使用包装类。

DatabaseHelper 包装器的示例

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

和来自 SQLiteDatabase 包装器的简短片段

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

在 robolectric 测试中,我将每个反射的 USE_UNSECURE_DATABASE 设置为 true

于 2014-08-06T07:09:32.490 回答