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

如何在C语言中无getter/setter时对ADT进行单元测试?

嵌入式C中ADT单元测试的干净实践方案?

我正在重构嵌入式C代码,遵循James W. Grenning所著《Test-Driven Development for Embedded C》中的建议,将代码按**抽象数据类型(ADT)**划分为模块,类似Java中的类。但遇到一个实际问题:很多ADT并不需要getter和setter,核心只需要两个操作——从字节数组提取数据构建结构体,以及将ADT存储的数据显示在设备屏幕上(显示逻辑依赖外部静态库和屏幕驱动)。

我希望对提取字节数组数据并构建ADT的extract_tlv函数做单元测试,但由于ADT封装了结构体成员,没有暴露getter/setter,无法直接验证内部状态;如果调用display_tlv来测试,又需要在模拟器运行并mock驱动,完全不适合做单元测试。

以TLV(tag length value)缓冲区的ADT为例:

tlv.h

struct tlv typedef tlv_t;

// 从字节缓冲区中提取TLV数据,构建tlv_t结构体
tlv_t * extract_tlv(const uint8_t *buffer, size_t buffer_length);

// 将tlv结构体中的数据显示在设备屏幕上
int display_tlv(const tlv_t *tlv);

tlv.c

#include "tlv.h"

typedef enum
{
  TAG_A,
  TAG_B,
  ...
} tlv_tag_t;

struct tlv {
  tlv_tag_t tag;
  size_t length;
  uint8_t *value;
};


tlv_t * extract_tlv(const uint8_t *buffer, size_t buffer_length) {
   tlv_t *tlv = (tlv_t *)calloc(1, sizeof(tlv_t));
   if(!tlv)
   {
      return NULL;
   }

   // 提取缓冲区中的TLV数据并存储到tlv结构体
   ...

   return tlv;
}


int display_tlv(const tlv_t *tlv) {
   // 访问tlv结构体字段并执行显示逻辑
   ...
}

假设输入缓冲区为0x0001000412345678(tag和length为uint16_t类型),调用extract_tlv后预期得到的结构体状态为:

tlv.tag    = TAG_B,                      // 对应0x0001
tlv.length = 4,                          // 对应0x0004
tlv.value  = {0x12, 0x34, 0x56, 0x78},   // 对应0x12345678

已尝试的方案与疑惑

我们曾尝试在tlv.h中用宏定义结构体成员,以此在测试代码中“绕过”封装:

修改后的tlv.h

#define TLV_STRUCT_MEMBER \
  tlv_tag_t tag; \
  size_t length; \
  uint8_t *value;

typedef enum
{
  TAG_A,
  TAG_B,
  ...
} tlv_tag_t;

struct tlv typedef tlv_t;

// 从字节缓冲区中提取TLV数据,构建tlv_t结构体
tlv_t * extract_tlv(const uint8_t *buffer, size_t buffer_length);

// 将tlv结构体中的数据显示在设备屏幕上
int display_tlv(const tlv_t *tlv);

修改后的tlv.c

#include "tlv.h"

struct tlv {
  TLV_STRUCT_MEMBER 
};


tlv_t * extract_tlv(const uint8_t *buffer, size_t buffer_length) {
   ...
}


int display_tlv(const tlv_t *tlv) {
   ...
}

测试代码test_tlv.c

#include "tlv.h"

struct test_tlv {
  TLV_STRUCT_MEMBER 
} typedef test_tlv_t;


TEST_EXTRACT_TLV() {
  test_tlv_t *tlv = (test_tlv_t *)extract_tlv(...);
  TEST_ASSERT_EQUAL(4, tlv->length);
  ...
}

这种通过强制类型转换访问结构体成员的方式感觉很hacky,我并不认可。另外我也有疑惑:只为单元测试实现getter和setter,属于“干净”的实践吗?

请问这种场景下的最佳“干净”实践是什么?有没有合适的解决方案?

内容的提问来源于stack exchange,提问作者kokopelli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 08:08:15