在 Android JUnit 测试中加载本机库 [英] Loading a native library in an Android JUnit test

查看:25
本文介绍了在 Android JUnit 测试中加载本机库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 ndk-build 生成了一个本机库,我可以在我的 Android 应用程序中加载和使用它.但是,我想针对我应用的这一部分编写一些测试.

I've generated a native library using ndk-build which I'm able to load and use with in my Android application. However, I want to write some tests against this part of my app.

在我的测试中调用本机函数时,我收到此异常消息:

When calling the native function in my tests, I recieve this exception message:

java.lang.UnsatisfiedLinkError: no process in java.library.path

...其中 process 是我要导入的本机库,名为 libprocess.so.

...where process is my native library to import, named libprocess.so.

我正在使用 Roboelectric 进行我的测试,并使用 RobolectricTestRunner 运行这个特定的测试,如果它有所不同的话.

I'm using Roboelectric for my tests, and running this particular one with the RobolectricTestRunner, if it makes a difference.

如何让我的测试项目看到"本机库?

How can I get my test project to 'see' the native library?

我正在我的应用程序中加载库,如下所示:

I'm loading the library in my app like so:

static {
    System.loadLibrary("process");
}
public static native int[] process(double[][] data);

在应用程序中调用 Process.process(array) 工作正常(库已加载),但从测试运行时失败,上面给出的异常.

calling Process.process(array) works fine in the app (the library is loaded), but fails when run from the tests with the exception given above.

编辑 2:如果我将 -Djava.library.path="" 设置为 VM 参数,则:

Edit 2: If I set -Djava.library.path="<the directory of libprocess.so>" as a VM argument, then:

System.out.println(System.getProperty("java.library.path"));

确实显示了我设置的路径,但我仍然遇到相同的异常.我将目录设置为:

does show the path I set, but I still get the same exception. I'm setting the directory as:

<project-name>/libs/x86

...但作为绝对路径.

...but as an absolute path.

推荐答案

对于任何仍在寻找的人,blork 有正确的想法 - 您需要为您的本机"平台(Windows、Linux、Mac)编译本机库.Android NDK 为 Android 平台构建库(.so 文件 - 也可能适用于 Linux),这就是为什么在活动测试用例中运行没有问题的原因(因为它会加载一个 Android 实例).

For anyone still looking, blork had the right idea - you need to compile your native libraries for your 'native' platform (Windows, Linux, Mac). The Android NDK builds libraries for the Android platform (.so files - might also work on Linux), and this is why there are no issues running in Activity Test Cases (because it loads up an Android instance).

要运行低级、极快的 JUnit 测试,您需要支持 JVM.在 Windows 上,这可能是构建 DLL,在 Apple 上,它是构建 dylib(假设共享库).

To get the low-level, hella fast JUnit tests running, you need to support your JVM. On Windows, this might be building DLLs, on Apple, it's building dylibs (assuming shared libraries).

我刚刚在我的 android-ndk-swig-example 存储库中完成了一个示例(https://github.com/sureshjoshi/android-ndk-swig-example/issues/9).

I've just completed a sample in my android-ndk-swig-example repo (https://github.com/sureshjoshi/android-ndk-swig-example/issues/9).

基本上,在我的 CMakeLists 中,我添加了 Apple 警告:

Basically, in my CMakeLists, I added an Apple caveat:

# Need to create the .dylib and .jnilib files in order to run JUnit tests
if (APPLE)
    # Ensure jni.h is found
    find_package(JNI REQUIRED)
    include_directories(${JAVA_INCLUDE_PATH})

然后我确保 Gradle 运行进行单元测试,但使用 Mac 构建系统(不是 NDK).

And then I make sure Gradle runs for unit tests, but using the Mac build system (not NDK).

def osxDir = projectDir.absolutePath + '/.externalNativeBuild/cmake/debug/osx/'

task createBuildDir() {
    def folder = new File(osxDir)
    if (!folder.exists()) {
        folder.mkdirs()
    }
}

task runCMake(type: Exec) {
    dependsOn createBuildDir
    workingDir osxDir // Jump to future build directory
    commandLine '/usr/local/bin/cmake' // Path from HomeBrew installation
    args '../../../../' // Relative path for out-of-source builds
}

task runMake(type: Exec) {
    dependsOn runCMake
    workingDir osxDir
    commandLine 'make'
}

 project.afterEvaluate {
    // Not sure how much of a hack this is - but it allows CMake/SWIG to run before Android Studio
    // complains about missing generated files
    // TODO: Probably need a release hook too?
    javaPreCompileDebug.dependsOn externalNativeBuildDebug
    if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
        javaPreCompileDebugAndroidTest.dependsOn runMake
    }
 }

注意时间!!!

当您使用这种方法时,从技术上讲,您不是在测试 NDK 生成的库.您正在测试相同的代码,但使用不同的编译器(msvc、xcode、gcc、clang,无论您在主机上使用什么)编译.

When you use this method, you're technically not testing the NDK-generated libs. You're testing the same code, but compiled using a different compiler (msvc, xcode, gcc, clang, whatever you use on the host).

这实际上意味着大多数测试结果都是有效的——除非你遇到由每个编译器的怪癖或 STL 实现等引起的问题......这并不像 10+ 时那么糟糕多年前,但您不能 100% 肯定地说使用主机库进行 JUnit 测试的结果与 Android 库相同.不过,你可以说它相当接近.

What this means practically, is that most of the test results will be valid - except when you run into problems caused by each compiler's quirks, or STL implementations, etc... This isn't as bad as it was 10+ years ago, but you can't say with 100% certainty that the results of JUnit testing with host libs is identical to the Android libs. You can say that it's reasonably close, though.

再说一次,除非您使用 Android NDK 为每个受支持的架构运行您的本机单元测试,否则您也不能说任何确定性...

Then again, unless you're running your native unit tests using the Android NDK for each supported architecture, you also can't say anything about certainty either... So take what you will from it.

一种矫枉过正的方法(但如果自动化的话真的很酷)是编写你的原生单元测试,不管你怎么做(谷歌测试、Catch 等),然后编译和运行你的原生库和每个使用 Android NDK 的单元测试建筑学.这提供了跨潜在目标架构的 C/C++ 覆盖.

An overkill approach (but really cool if automated) would be to write your native unit tests however you do them (Google Test, Catch, etc), then compile and run your native libs and unit tests with the Android NDK per each architecture. This provides your C/C++ coverage across your potential target architectures.

从这里开始,您可以将上述主机库与 JUnit 结合使用,以快速对 JNI 层与您的本机库交互进行单元测试.在您的 CI 系统中,您可能仍然应该运行这些相同的单元测试 - 但作为 Android Instrumentation 测试(或其他运行模拟 Android 环境的测试).

From here, you could use the aforementioned host libs with JUnit to rapidly unit test your JNI layer interacting with your native lib. In your CI system, you should still probably run these same unit tests - but as Android Instrumentation tests (or something else that runs an emulated Android environment).

与任何事情一样,无论您有什么界面,都可以创建模拟 - 但在某些时候,您也需要进行系统/功能/集成测试.

As with everything, wherever you have an interface, you can create mocks - but at some point, you'll need system/functional/integration tests too.

更新:

在博客文章(http://www.sureshjoshi.com/mobile/android-junit-native-libraries/)

这篇关于在 Android JUnit 测试中加载本机库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆