Android NDK with Google Test

Just to add to Alex's excellent answer, you can also deploy and run the resulting test binary using adb by adding the following to your CMakeLists.txt:

find_program(ADB adb)
add_custom_command(TARGET footest POST_BUILD
    COMMAND ${ADB} shell mkdir -p /data/local/tmp/${ANDROID_ABI}
    COMMAND ${ADB} push $<TARGET_FILE:native-lib> /data/local/tmp/${ANDROID_ABI}/
    COMMAND ${ADB} push $<TARGET_FILE:footest> /data/local/tmp/${ANDROID_ABI}/
    COMMAND ${ADB} shell \"export LD_LIBRARY_PATH=/data/local/tmp/${ANDROID_ABI}\; /data/local/tmp/${ANDROID_ABI}/footest\")

Note that in the above example footest is dependent on the shared library native-lib which is why we push that. The path to native-lib is specified by setting the LD_LIBRARY_PATH environment variable.


If you choose cmake to drive your externalNativeBuild (and this is the preferred option, according to Android Developers NDK guide), then you can simply add the following lines to your CMakeLists.txt:

set(GOOGLETEST_ROOT ${ANDROID_NDK}/sources/third_party/googletest/googletest)
add_library(gtest STATIC ${GOOGLETEST_ROOT}/src/gtest_main.cc ${GOOGLETEST_ROOT}/src/gtest-all.cc)
target_include_directories(gtest PRIVATE ${GOOGLETEST_ROOT})
target_include_directories(gtest PUBLIC ${GOOGLETEST_ROOT}/include)

add_executable(footest src/main/jni/foo_unittest.cc)
target_link_libraries(footest gtest)

If your build succeeds, you will find app/.externalNativeBuild/cmake/debug/x86/footest. From here, you can follow the instructions in README.NDK to run it on emulator or device.


Notes:

  • make sure that the ABI matches the target you use (the guide is not very clear about this).
  • the list of ABI's that are built is controlled by abiFilters in build.gradle. In Android Studio, even ndk-build ignores APP_ABI set in Application.mk.
  • the files Android.mk and Application.mk are ignored when you use cmake.
  • for gradle-3.3, and classpath 'com.android.tools.build:gradle:2.3.3', as in the current Android Studio release 2.3.3, you may need to explicitly specify the unittest target in build.gradle:

    android { defaultConfig { externalNativeBuild { cmake { targets "foo_unittest" }}}}
    
  • with Android Studio 3.0, gradle-4.1, and classpath 'com.android.tools.build:gradle:3.0.0-beta6' the executable is easier to find under app/build/intermediates/cmake/debug/obj.


To test the foo(int x, int y) function from foo.cpp in a shared library (to make is as close as possible to the NDK instructions), you need some more lines in your CMakeLists.txt script:

# build libfoo.so
add_library(foo SHARED src/main/jni/foo.cpp)
target_link_libraries(footest foo) 

You will find libfoo.so to copy manually to your device under app/build/intermediates/cmake/debug/obj.

To reduce the hassle, you can use STATIC instead of SHARED, or simply add foo.cpp to footest executable:

add_executable(footest src/main/jni/foo_unittest.cc src/main/jni/foo.cpp)