嵌入式C单元测试:含SDK依赖模块的测试方法问询
问题描述
我用C语言开发一款内置BLE核心的微控制器项目,依赖特定SDK和项目模板。现在用Unity测试框架测试自有模块时,因为模块大量引用SDK中的函数(比如BLE数据发送函数),出现编译引用问题。想请教:
- 这类模块该如何测试?
- 是否需要Mock所有SDK函数?
- 是否需要调整模块的编写方式?
模块示例代码
my_module.c
#include "sdk_module_1.h" #include "my_module.h" void init_hardware(void) { // 硬件初始化代码 } bool send_data(int data) { // 数据预处理,比如自增 data++; // 调用SDK中的send_data函数发送数据 return send_data(data); }
my_module.h
void init_hardware(void); bool send_data(int data);
my_module_test.c
#include "my_module.h" #include "unity.h" TEST_SETUP(Test) { } TEST_TEAR_DOWN(Test) { } TEST(Test, First_test) { TEST_ASSERT_EQUAL(true, send_data(5)); }
解决方案
1. 编译问题解决与测试思路
你遇到的编译问题本质是测试环境缺少SDK的编译依赖(头文件实现、库文件)。核心解决思路是Mock模块实际调用的SDK函数,不需要Mock所有SDK函数——只需要处理模块代码里直接用到的那些SDK接口。
2. 具体Mock实现方法
方法一:手动Mock(适合简单场景)
利用C语言的符号优先级,在测试代码中定义与SDK函数同名的Mock函数,覆盖SDK的原始实现:
// my_module_test.c #include "my_module.h" #include "unity.h" // Mock SDK的send_data函数 static int mock_call_count = 0; static int mock_last_param = 0; bool send_data(int data) { mock_call_count++; mock_last_param = data; return true; // 根据测试需求返回固定值 } TEST_SETUP(Test) { // 每次测试前重置Mock状态 mock_call_count = 0; mock_last_param = 0; } TEST_TEAR_DOWN(Test) { } TEST(Test, First_test) { TEST_ASSERT_EQUAL(true, send_data(5)); } TEST(Test, Verify_SDK_Call) { send_data(5); // 验证模块是否正确处理了数据并调用SDK TEST_ASSERT_EQUAL(1, mock_call_count); TEST_ASSERT_EQUAL(6, mock_last_param); }
方法二:用CMock自动生成Mock(适合复杂场景)
CMock是Unity配套的Mock框架,可根据SDK头文件自动生成Mock函数,支持验证调用次数、参数、动态设置返回值:
- 配置CMock脚本,指定要Mock的SDK头文件(如
sdk_module_1.h) - 生成
Mock_sdk_module_1.c和Mock_sdk_module_1.h文件 - 在测试代码中使用CMock的API设置预期:
#include "my_module.h" #include "unity.h" #include "Mock_sdk_module_1.h" TEST(Test, First_test) { // 设置预期:SDK的send_data会被调用1次,参数为6,返回true send_data_ExpectAndReturn(6, true); TEST_ASSERT_EQUAL(true, send_data(5)); }
3. 模块编写方式优化建议
为了让模块更易测试,建议做以下调整:
- 依赖注入:将SDK函数通过函数指针传入模块,解耦模块与SDK的直接依赖:
// my_module.c #include "my_module.h" typedef bool (*send_data_func_t)(int); static send_data_func_t s_send_data_func; // 初始化时注入SDK函数指针 void my_module_init(send_data_func_t send_data_func) { s_send_data_func = send_data_func; } bool send_data(int data) { data++; return s_send_data_func(data); }
测试时直接传入Mock函数即可:
bool mock_send(int data) { return true; } TEST(Test, Inject_Mock) { my_module_init(mock_send); TEST_ASSERT_EQUAL(true, send_data(5)); }
分层设计:将模块拆分为业务逻辑层和SDK适配层。业务层只处理数据逻辑,不直接调用SDK;适配层专门负责与SDK交互。测试时只需聚焦业务逻辑层,适配层做简单集成测试即可。
预编译宏隔离:用宏定义切换测试/生产环境的依赖:
// my_module.c #include "my_module.h" #ifdef TEST_ENV #include "mock_sdk_module_1.h" #else #include "sdk_module_1.h" #endif bool send_data(int data) { data++; return send_data(data); }
编译测试代码时添加-DTEST_ENV参数,自动切换为Mock依赖。
内容的提问来源于stack exchange,提问作者Adam Stróż
相关产品推荐
相关产品推荐

