You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入式C单元测试:含SDK依赖模块的测试方法问询

问题描述

我用C语言开发一款内置BLE核心的微控制器项目,依赖特定SDK和项目模板。现在用Unity测试框架测试自有模块时,因为模块大量引用SDK中的函数(比如BLE数据发送函数),出现编译引用问题。想请教:

  1. 这类模块该如何测试?
  2. 是否需要Mock所有SDK函数?
  3. 是否需要调整模块的编写方式?

模块示例代码

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函数,支持验证调用次数、参数、动态设置返回值:

  1. 配置CMock脚本,指定要Mock的SDK头文件(如sdk_module_1.h)
  2. 生成Mock_sdk_module_1.c和Mock_sdk_module_1.h文件
  3. 在测试代码中使用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óż

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 13:40:10