Android NDK 原生代码单元测试实践:基于 googletest 与 junit-gtest 的设备端测试指南(ndk-samples unit-test 深度解析)

发布时间:2026/9/24 17:16:40

Android NDK 原生代码单元测试实践:基于 googletest 与 junit-gtest 的设备端测试指南(ndk-samples unit-test 深度解析) 示例工程移动开发【免费下载链接】ndk-samplesAndroid NDK samples with Android Studio项目地址https://gitcode.com/gh_mirrors/nd/ndk-samples点击查看免费下载在 Android 开发中C/C 原生代码NDK的正确性往往依赖人工验证或集成测试兜底而官方推荐的 googletest 框架在移动设备上的落地方式一度缺乏开箱即用的示例。本指南以 ndk-samples 仓库中的 unit-test 示例为蓝本完整讲解如何用 googletest 为 NDK 原生代码编写单元测试并通过 junit-gtest 桥接层让测试在手机或模拟器上以 Android Instrumented Test 的形式运行。读完本文你将掌握从 CMake 配置、Gradle 依赖声明到 Kotlin 测试壳类编写的完整链路并能读懂失败断言在设备端的输出格式。一、示例整体结构一个被测库 测试库 桥接壳的三层模型unit-test 示例虽然业务逻辑极简一个两数相加函数却完整呈现了 NDK 单元测试的标准工程骨架。从仓库目录结构看它由三部分组成unit-test/app/src/main/cpp/ # 原生代码被测库与测试库 unit-test/app/src/main/java/ # 应用主代码JNI 入口与界面 unit-test/app/src/androidTest/ # 设备端测试壳JUnit 桥接被测库unittest由 adder.cpp 与 native-lib.cpp 编译而成通过 JNI 向 Kotlin 层暴露add方法测试库app_tests仅包含 adder_test.cpp链接 googletest 与 junit-gtest专门用于承载原生测试用例测试壳NativeTests位于 NativeTests.kt是一个空类通过注解把 googletest 用例翻译成 AndroidJUnitRunner 认识的 Instrumented Test。主界面 MainActivity.kt 在onCreate中调用add(1, 2)并显示结果native-lib.cpp 中的 JNI 函数把参数原样转发给add()。这个应用可用、测试也可用的设计正是关键被测代码同时服务于产品路径与测试路径杜绝了测试代码与应用代码分叉的问题。二、编写单元测试从被测函数到 googletest 用例2.1 被测代码保持头文件与实现分离被测库的核心是一个极简加法函数头文件 adder.h 只暴露声明#pragma once int add(int a, int b);实现 adder.cpp 同样直白#include adder.h int add(int a, int b) { return a b; }这里采用头文件 实现分离的经典布局让测试文件只依赖头文件即可编译也为后续用$TARGET_OBJECTS:adder复用目标文件埋下伏笔见第三节。2.2 测试用例gtest 宏的基本用法adder_test.cpp 是一个完整可编译的最小 gtest 用例#include adder.h #include gtest/gtest.h TEST(adder, adder) { EXPECT_EQ(3, add(1, 2)); }要点拆解TEST(TestSuiteName, TestName)是 googletest 定义测试用例的宏第一个参数为测试套件名第二个为用例名EXPECT_EQ(expected, actual)是非致命断言失败时记录错误但继续执行当前用例与ASSERT_EQ致命断言失败即中止形成互补。对单元测试而言EXPECT_*能一次跑出多个失败点更适合做行为校验测试文件通过#include gtest/gtest.h引入框架头文件而 googletest 头文件与库本身由 NDK 官方打包的 prefab 依赖提供详见第四节。三、构建配置CMake 侧如何织入测试库3.1 完整 CMakeLists 的职责划分unit-test/app/src/main/cpp/CMakeLists.txt 是理解整个构建体系的核心。它声明了三个构建目标cmake_minimum_required(VERSION 3.22.1) project(unittest) find_package(googletest REQUIRED CONFIG) find_package(junit-gtest REQUIRED CONFIG) # 被测对象库只编译不链接 add_library(adder OBJECT adder.cpp) # 应用共享库复用 adder 的目标文件 add_library(unittest SHARED $TARGET_OBJECTS:adder native-lib.cpp) # 测试共享库同样复用 adder 的目标文件并链接 gtest 与 junit-gtest add_library(app_tests SHARED adder_test.cpp) target_link_libraries(app_tests PRIVATE $TARGET_OBJECTS:adder googletest::gtest junit-gtest::junit-gtest )3.2 三个值得细读的设计点add_library(adder OBJECT ...)对象库adder.cpp被编译成目标文件而非独立库随后通过生成器表达式$TARGET_OBJECTS:adder同时注入unittest与app_tests两个库。这意味着同一份被测源码既进入 APK 内的应用库也进入测试库避免了两份编译产物行为不一致的隐患也无需为测试单独复制源码。find_package(... REQUIRED CONFIG)googletest 与 junit-gtest 都通过 prefab 机制以 CMake 包形式提供CONFIG模式从依赖的包目录加载*Config.cmake找到后即可使用googletest::gtest、junit-gtest::junit-gtest这两个 imported target 完成链接。app_tests是 SHARED 而非可执行文件在 Android 上原生测试不能以独立可执行文件直接运行缺少合适的进程宿主因此必须编译为.so并打入测试 APK再由 JVM 侧的 runner 加载执行——这正是 junit-gtest 桥接层存在的根本原因。3.3 为什么native-lib.cpp不进入测试库注意app_tests只链接了adder_test.cpp和adder对象库而native-lib.cppJNI 胶水属于应用库。从源码结构看这是刻意为之单元测试应聚焦纯逻辑函数不依赖 JNIEnv、jobject 等运行时环境把 JNI 层从测试范围中剥离让用例在设备上以最轻量方式运行。四、Gradle 配置prefab 开关、依赖版本与测试库隔离4.1 开启 prefab 并声明依赖unit-test/app/build.gradle 中完成了两件事。首先因为 googletest 通过 prefab 分发必须在android块中开启该特性buildFeatures { viewBinding true prefab true }其次在dependencies中引入桥接库与测试框架示例通过 gradle/libs.versions.toml 统一管理版本dependencies { implementation libs.androidx.junit.gtest // androidx.test.ext:junit-gtest:1.0.0-alpha02 implementation libs.googletest // com.android.ndk.thirdparty:googletest:1.11.0-beta-1 ... }对应版本目录gradle/libs.versions.toml中的实际定义为androidxJUnitGTest 1.0.0-alpha02 googletest 1.11.0-beta-1 androidx-junit-gtest { group androidx.test.ext, name junit-gtest, version.ref androidxJUnitGTest } googletest { group com.android.ndk.thirdparty, name googletest, version.ref googletest }两点说明com.android.ndk.thirdparty:googletest是NDK 官方预构建的 prefab 包内含各 ABI 的libgtest及 CMake 配置文件因此无需在源码树中手动 vendoring googletest版本以当前仓库实际内容为准README 中写的1.0.0-alpha01在版本目录中已升级为1.0.0-alpha02googletest 为1.11.0-beta-1。4.2 测试库隔离packagingOptions的关键作用app/build.gradle中有一处容易被忽略但至关重要的配置packagingOptions { jniLibs { // Gradle has no way of knowing which of the libraries in our // CMakeLists.txt are for the app and which are for tests, so we // have to tell it which libraries are test libraries. Without // this, the test libraries will end up packaged in the real API // and not just the test APK. testOnly [**/libapp_tests.so] } }注释说得很清楚Gradle 无法从 CMakeLists 推断哪些库服务于应用、哪些服务于测试。若不加testOnlylibapp_tests.so会被打进正式 APK白白增大安装包体积。把**/libapp_tests.so标记为testOnly后它只进入测试 APK。如果你复制本项目务必把这里改成自己测试库的名字如libmytests.so。4.3 Instrumentation RunnerdefaultConfig中还声明了testInstrumentationRunner androidx.test.runner.AndroidJUnitRunner这是运行设备端测试所必需的 runner它与NativeTests壳类配合完成 googletest 用例的调度与结果回传。五、Kotlin 桥接壳把 gtest 用例暴露给 AndroidJUnitRunner原生测试库编译完成后还需要一个翻译层让 JVM 侧的测试框架发现并执行它。NativeTests.kt 全文如下package com.example.unittest import androidx.test.ext.junitgtest.GtestRunner import androidx.test.ext.junitgtest.TargetLibrary import org.junit.runner.RunWith RunWith(GtestRunner::class) TargetLibrary(libraryName app_tests) class NativeTests逐行解读RunWith(GtestRunner::class)把标准 JUnit4 runner 替换为 androidx 提供的GtestRunner其职责是加载指定原生库、枚举其中的 googletest 用例并逐一执行再把通过/失败结果映射回 JUnit 的测试报告TargetLibrary(libraryName app_tests)指定要加载的测试共享库名。注意不包含lib前缀与.so后缀——GtestRunner会自动补全为libapp_tests.soclass NativeTests类体为空即可所有测试逻辑都在 C 侧。若后续新增多个测试库可对应增加多个带不同TargetLibrary的壳类。这个壳类必须放在androidTest源集unit-test/app/src/androidTest/java/com/example/unittest/NativeTests.kt因为它属于 Instrumented Test需要运行在设备上。六、在设备上运行测试与解读失败输出6.1 运行方式在 Android Studio 中直接对NativeTests类或包右键选择Run即可在连接的手机或模拟器上执行这与 Android 官方 Instrumented Test 的运行方式完全一致也可用命令行./gradlew :unit-test:app:connectedDebugAndroidTest6.2 失败断言的输出格式示例特意演示了故意破坏测试的情形——把断言改成EXPECT_EQ(4, add(1, 2))EXPECT_EQ(4, add(1, 2));运行后会在测试报告中看到类似如下输出java.lang.AssertionError: /path/to/ndk-samples/unit-test/app/src/main/cpp/adder_test.cpp:6 Expected equality of these values: 4 add(1,2) Which is: 3这段输出信息量很大值得逐字段解读java.lang.AssertionErrorgoogletest 的失败通过 junit-gtest 转换为 JVM 侧的断言异常从而被 JUnit 报告捕获源码位置adder_test.cpp:6精确指向触发断言的测试文件与行号此处即EXPECT_EQ所在行便于快速定位Expected equality of these values:是EXPECT_EQ的标准失败模板随后分别列出期望值与实际表达式Which is: 3给出add(1,2)的实际计算结果即失败根因是真实返回值 3 与期望值 4 不符。这种期望值 / 实际表达式 / 实际值 / 源文件行号四位一体的报错结构是 gtest 优于裸printf调试的核心价值所在。修复断言为EXPECT_EQ(3, add(1, 2))后测试即恢复绿色。七、运行效果与工程启示上图是示例在设备上的实际运行效果界面通过 JNI 调用add(1, 2)展示 1 2 3。它同时印证了本示例同一份add()既服务应用、又服务测试的设计——应用的显示逻辑与测试用例共享同一实现任何一方改动都能立即在另一方暴露问题。从工程实践角度看该示例给出了三条可复用的方法论用对象库 $TARGET_OBJECTS:复用被测代码从构建层面杜绝测试副本与产品代码分叉用 prefab 引入预构建的 googletest免去源码级集成 gtest 的维护成本用testOnly隔离测试库确保测试产物不污染正式 APK。八、延伸阅读本示例完整源码unit-test/app/src/main/cpp/CMakeLists.txt、unit-test/app/src/androidTest/java/com/example/unittest/NativeTests.kt、unit-test/app/build.gradle版本与依赖统一管理gradle/libs.versions.toml仓库内其他 NDK 实战示例的架构说明ARCHITECTURE.md补充说明本文涉及的 googletest 与 junit-gtest 版本以当前仓库 gradle/libs.versions.toml 为准googletest1.11.0-beta-1、junit-gtest1.0.0-alpha02。alpha/beta版本表明 API 仍在演进生产项目接入前建议关注其正式版本发布情况。赞分享示例工程移动开发【免费下载链接】ndk-samplesAndroid NDK samples with Android Studio项目地址https://gitcode.com/gh_mirrors/nd/ndk-samples点击查看免费下载相关推荐在真机上跑C单元测试ndk-samples unit-test样本googletest原生测试深度解析在真机上跑C单元测试ndk samples unit test样本googletest原生测试深度解析 Android 原生代码怎么写单元测试 ndk示例工程移动开发Android NDK Samples深度解析入门JNI与基础开发实践Android NDK Samples深度解析入门JNI与基础开发实践 Android NDK Samples项目是Google官方维护的综合性示例代码库专示例工程移动开发OpenSSL 单元测试实战指南基于 cmocka 与 --wrap 的隔离测试体系test/unitOpenSSL 单元测试实战指南基于 cmocka 与 wrap 的隔离测试体系test/unit OpenSSL 作为一个承载 TLS 与密码学核心逻辑密码学网络安全通信上一篇StaffML 语料库北极星 v3如何从四个原则性约束推导出 12,000–14,000 道 ML 系统面试题下一篇Ente Photos 画廊模式Gallery Mode完全指南无账号的端侧本地相册体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/24 17:16:40

基于SpringBoot的智慧泊车系统的设计与实现毕业设计项目源码

联系博主 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 …

2026/9/24 17:11:39

电碳表与碳资产管理平台:双碳目标下的智慧能源管理新范式

摘要:电碳表依托智能计量与物联网技术,实现用电全流程碳排放动态精准测算,为碳排放核算提供实时、可信的数据支撑;碳资产管理平台则以精准碳核算为基础,对接碳交易市场,助力企业盘活碳资产、创造碳收益。二…

2026/9/24 17:11:39

HT7183(3A开关、16V输出DC-DC升压IC) 聚能芯半导体一级代理

概述HT7183作为一款高功率异步升压转换器,集成了低阻值达120mΩ的功率开关管,为便携式系统量身打造了高效且紧凑的解决方案。该转换器的输入电压范围覆盖2.6V至5.5V,能够为各类采用不同供电方式的应用提供有力支持。HT7183具备卓越的性能&…

2026/9/24 18:21:44

Spring Boot后端项目部署实战:从解压到联调的全流程指南

简介:期刊出版数字化要求后端系统高效组织数据与业务逻辑。这份资源正是一套面向初中级开发者的期刊管理后端实现,适合用来学习API设计、数据库建模与权限控制。资源围绕期刊、文章、作者、审稿人等核心实体,以Python提供app入口、rpc远程调用…

2026/9/24 18:21:44

AI创业公司云平台选型指南:算力、成本与防锁定策略

这两年我经常被VC朋友问同一个问题:手上投了十几家AI公司,每家都在问云平台怎么选,能不能直接给个清单?说实话,这个问题没有标准答案,但问的人多了,我发现大家踩过的坑高度重合。今天这篇就从技…

2026/9/24 18:21:44

Win11网线直连传大文件:“输入网络凭据”问题全解析

1. 为什么网线直连才是最稳的文件传输方式先说个场景:两台电脑都需要互传大量文件,一个大活儿是几十 GB 的设计稿、视频素材或者虚拟机镜像。用 U 盘倒腾来回拔插累得够呛,走微信、网盘传大文件要么限速要么压缩画质,内网 WiFi 传…

2026/9/24 18:21:44

从COCO到YOLO:雨雪路面数据集训练全流程与避坑指南

简介:雨雪天气路面状况识别是自动驾驶与智能交通中的常见难点,这份数据集专门面向结冰路面、雪地、下雨湿滑、干燥路面四类场景,图片均为原始拍摄图像,并使用COCO格式进行目标标记,可直接用于目标检测、语义分割等模型…

2026/9/24 18:16:44

Java火车票系统实战:解决超卖、事务隔离与订单唯一性

简介:这是一套面向Java初学者与数据库课程实践者的火车票售票系统完整源码,基于Java Swing界面与Access数据库(.mdb文件)实现,解决小型票务场景下的车次管理、余票查询、在线售票与退票等核心业务需求。资源共89个文件…

2026/9/23 12:07:00

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/24 0:00:21

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:21

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:21

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码