ESP8266 Arduino平台C++类单元测试方案咨询
当然可以搞定!针对ESP8266 Arduino平台上的C类做单元测试,确实得绕开标准C测试库的一些限制(比如Arduino不支持异常),不过有几个非常贴合你需求的方案,我来给你拆解一下:
1. 轻量级断言与测试结构:用AUnit库
AUnit是专门为Arduino生态设计的单元测试库,完美适配ESP8266,完全不需要依赖异常,还支持你需要的测试套件、测试用例结构,以及丰富的断言功能。
上手步骤:
- 在Arduino库管理器里搜索并安装
AUnit - 编写测试代码的示例:
#include <AUnit.h> // 引入你要测试的类 #include "TemperatureSensor.h" // 定义测试套件和测试用例 test(TemperatureSensorTests, testReadTemperature) { TemperatureSensor sensor; // 断言读取的温度在合理范围内 assertBetween(sensor.read(), 20.0, 30.0); // 断言传感器初始化状态正确 assertEqual(sensor.isInitialized(), true); } test(TemperatureSensorTests, testCalibration) { TemperatureSensor sensor; sensor.calibrate(25.0); assertEqual(sensor.read(), 25.0); } void setup() { Serial.begin(115200); // 启动测试运行器 aunit::TestRunner::run(); } void loop() { // 测试运行器的循环逻辑,用于处理异步测试(如果需要) aunit::TestRunner::loop(); }
- 上传代码到ESP8266后,打开串口监视器就能看到测试结果,包括每个用例的通过/失败状态。
AUnit支持setup()/teardown()方法,你可以在每个测试用例前后做初始化或清理操作,非常方便。
2. 方法调用验证的模拟功能
针对模拟需求,有两个实用方向:
手动Mock(适合简单场景)
如果你的类依赖其他组件(比如传感器、外设驱动),可以自己写一个Mock类来记录方法调用的次数、参数,然后在测试里验证。示例如下:
// 模拟一个串口输出类 class MockSerial { public: int writeCount = 0; String lastMessage; size_t write(const char* msg) { writeCount++; lastMessage = String(msg); return strlen(msg); } }; // 测试依赖串口的Logger类 test(LoggerTests, testLogMessage) { MockSerial mockSerial; Logger logger(&mockSerial); logger.log("Hello Test"); // 验证write方法被调用了1次 assertEqual(mockSerial.writeCount, 1); // 验证输出的消息正确 assertEqual(mockSerial.lastMessage, "Hello Test"); }
这种方式简单直接,不需要额外依赖库,适合小项目或简单的依赖模拟。
使用FakeIt库(适合复杂模拟)
FakeIt是一个轻量级的C++ Mocking库,可以配置成不依赖异常,完美适配ESP8266。你需要把FakeIt的源码加入到项目中,然后在ESP8266的编译选项里添加-fno-exceptions(关闭异常支持)。
示例代码:
#include "FakeIt.h" // 定义你要模拟的接口 #include "IButton.h" // 引入依赖该接口的类 #include "ButtonHandler.h" using namespace fakeit; test(ButtonHandlerTests, testButtonPress) { // 创建接口的Mock对象 Mock<IButton> mockButton; // 设置Mock的行为:当调用isPressed()时返回true When(Method(mockButton, isPressed)).Return(true); // 把Mock对象注入到ButtonHandler中 ButtonHandler handler(mockButton.get()); // 执行测试逻辑 bool result = handler.checkButtonState(); // 验证isPressed()方法被调用了一次 Verify(Method(mockButton, isPressed)).Once(); // 断言结果正确 assertEqual(result, true); }
FakeIt支持更复杂的Mock场景,比如指定参数匹配、多次调用的不同返回值等,适合有复杂依赖的类测试。
3. 离线测试(推荐方案)
如果你的类核心逻辑和Arduino硬件API(比如digitalRead、Serial)耦合度不高,强烈建议把核心逻辑抽离成不依赖Arduino环境的纯C++类,然后在标准C++环境(比如VSCode+GCC、CLion)里用成熟的测试库测试,比如:
- Catch2:可以通过配置关闭异常支持,语法简洁,支持测试套件和丰富断言
- Google Test:同样可以关闭异常,功能强大,适合大型项目
比如,你可以把硬件操作抽象成接口,核心类依赖接口而不是具体的Arduino实现:
// 抽象硬件接口(纯C++,无Arduino依赖) class ILedDriver { public: virtual void turnOn() = 0; virtual void turnOff() = 0; }; // 核心逻辑类(依赖接口) class LedController { private: ILedDriver* _driver; public: LedController(ILedDriver* driver) : _driver(driver) {} void toggle() { // 核心逻辑... } }; // Arduino环境下的具体实现 class ArduinoLedDriver : public ILedDriver { private: int _pin; public: ArduinoLedDriver(int pin) : _pin(pin) { pinMode(pin, OUTPUT); } void turnOn() override { digitalWrite(_pin, HIGH); } void turnOff() override { digitalWrite(_pin, LOW); } };
这样你可以在标准C++环境里用Mock框架测试LedController的核心逻辑,测试效率更高,还能利用IDE的调试功能,之后再把测试好的代码放回Arduino项目中对接硬件实现。
内容的提问来源于stack exchange,提问作者zak

