Rust嵌入式测试宏库embedded-test-macros的使用:简化嵌入式开发单元测试与集成测试流程
Rust嵌入式测试宏库embedded-test-macros的使用:简化嵌入式开发单元测试与集成测试流程
概述
embedded-test-macros
是为 embedded-test
库提供的宏。请注意不要直接使用这个 crate,这些宏已经被 embedded-test
crate 重新导出。
安装
在项目目录中运行以下 Cargo 命令:
cargo add embedded-test-macros
或者在 Cargo.toml 中添加以下行:
embedded-test-macros = "0.6.1"
示例代码
以下是一个完整的嵌入式测试示例,展示了如何使用 embedded-test-macros
来简化单元测试和集成测试:
// 导入必要的宏和库
use embedded_test_macros::{embedded_test, test_setup};
use embedded_test::TestResult;
// 测试设置函数,会在所有测试前运行
#[test_setup]
fn setup() {
// 初始化硬件或设置测试环境
println!("Setting up test environment...");
}
// 简单的单元测试
#[embedded_test]
fn test_addition() -> TestResult {
assert_eq!(2 + 2, 4);
Ok(())
}
// 带有参数和返回值的测试
#[embedded_test]
fn test_with_parameters() -> TestResult {
let input = 5;
let expected = 25;
let result = input * input;
assert_eq!(result, expected);
Ok(())
}
// 模拟硬件交互的测试
#[embedded_test]
fn test_hardware_interaction() -> TestResult {
// 模拟读取传感器数据
let sensor_value = 42;
// 验证值在预期范围内
assert!(sensor_value > 0 && sensor_value < 100);
Ok(())
}
// 测试失败案例
#[embedded_test]
fn test_failure_case() -> TestResult {
// 这个测试会失败
assert_eq!(1, 2);
Ok(())
}
// 异步测试示例
#[embedded_test]
async fn test_async_operation() -> TestResult {
// 模拟异步操作
async {
// 一些异步代码
Ok(())
}.await?;
Ok(())
}
完整示例代码
//! 嵌入式测试完整示例
//! 展示如何使用embedded-test-macros进行复杂的嵌入式测试
#![no_std]
#![no_main]
use embedded_test_macros::{embedded_test, test_setup};
use embedded_test::TestResult;
use cortex_m_semihosting::hprintln;
// 初始化测试环境
#[test_setup]
fn setup() {
hprintln!("初始化硬件测试环境...").unwrap();
// 这里可以添加硬件初始化代码
// 例如:初始化GPIO、UART、定时器等
}
// 测试基本算术运算
#[embedded_test]
fn test_arithmetic() -> TestResult {
assert_eq!(10 - 5, 5);
assert_eq!(10 / 2, 5);
assert_eq!(10 % 3, 1);
Ok(())
}
// 测试硬件寄存器读写
#[embedded_test]
fn test_register_access() -> TestResult {
// 模拟寄存器地址
let mut register: u32 = 0;
// 写入值
register = 0xDEADBEEF;
// 验证读取值
assert_eq!(register, 0xDEADBEEF);
Ok(())
}
// 测试嵌入式中断处理
#[embedded_test]
fn test_interrupt_handling() -> TestResult {
// 模拟中断标志位
let mut interrupt_flag = false;
// 模拟中断触发
interrupt_flag = true;
// 验证中断处理
assert!(interrupt_flag);
Ok(())
}
// 测试硬件通信协议
#[embedded_test]
fn test_communication_protocol() -> TestResult {
// 模拟SPI数据传输
let tx_data: [u8; 4] = [0xAA, 0xBB, 0xCC, 0xDD];
let mut rx_data: [u8; 4] = [0; 4];
// 模拟数据传输过程
rx_data.copy_from_slice(&tx_data);
// 验证数据一致性
assert_eq!(tx_data, rx_data);
Ok(())
}
// 测试硬件定时器
#[embedded_test]
fn test_timer() -> TestResult {
// 模拟定时器计数值
let mut timer_counter = 0u32;
// 模拟定时器递增
for _ in 0..10 {
timer_counter += 1;
}
// 验证定时器计数
assert_eq!(timer_counter, 10);
Ok(())
}
// 测试异步硬件操作
#[embedded_test]
async fn test_async_hardware() -> TestResult {
// 模拟异步DMA传输
async {
// 模拟DMA传输完成
Ok::<(), &'static str>(())
}.await?;
Ok(())
}
测试输出
运行测试时,你会看到类似以下的输出:
初始化硬件测试环境...
Running test_arithmetic... OK
Running test_register_access... OK
Running test_interrupt_handling... OK
Running test_communication_protocol... OK
Running test_timer... OK
Running test_async_hardware... OK
特性
- 简化测试设置:通过
#[test_setup]
宏可以轻松设置测试环境 - 统一测试接口:所有测试都返回
TestResult
类型 - 支持异步测试:可以直接编写异步测试函数
- 嵌入式友好:专门为嵌入式环境设计,减少不必要的开销
- 清晰的测试报告:自动生成易读的测试结果输出
注意事项
- 确保在你的
Cargo.toml
中依赖embedded-test
而不是直接依赖embedded-test-macros
- 测试函数必须返回
TestResult
类型 - 可以使用
?
操作符在测试中传播错误 - 测试失败时会自动显示失败原因和位置
许可证
该项目采用 MIT 或 Apache-2.0 双重许可证。
1 回复
Rust嵌入式测试宏库embedded-test-macros使用指南
简介
embedded-test-macros
是一个专门为Rust嵌入式开发设计的测试宏库,旨在简化嵌入式系统中的单元测试和集成测试流程。它为嵌入式开发者提供了更符合硬件开发场景的测试工具,特别适合在资源受限的环境中编写和运行测试。
主要特性
- 提供嵌入式友好的测试宏
- 支持硬件相关测试场景
- 简化测试结构
- 与
cortex-m-rt
等嵌入式运行时良好集成 - 提供硬件感知的断言功能
安装
在Cargo.toml中添加依赖:
[dependencies]
embedded-test-macros = "0.1"
基本使用方法
1. 基本测试宏
use embedded_test_macros::embedded_test;
#[embedded_test]
fn test_adc_reading() {
let adc = Adc::new();
let reading = adc.read_channel(0);
assert_within_range!(reading, 0..=4095, "ADC读数超出预期范围");
}
2. 硬件初始化测试
#[embedded_test(setup = init_hardware)]
fn test_pwm_output() {
let pwm = Pwm::new();
pwm.set_duty_cycle(50);
assert_io_level!(PWM_PIN, High, "PWM输出不正确");
}
fn init_hardware() {
// 初始化所有硬件外设
init_clock();
init_gpio();
init_pwm();
}
3. 带超时的测试
#[embedded_test(timeout_ms = 100)]
fn test_sensor_response() {
let sensor = Sensor::new();
let response = sensor.query();
assert!(response.is_ok(), "传感器未在超时时间内响应");
}
高级功能
1. 硬件相关断言
#[embedded_test]
fn test_uart_transmission() {
let uart = Uart::new();
uart.write(b"test");
assert_io_sequence!(UART_RX_PIN, [High, Low, Low, High], "UART传输数据不匹配");
}
2. 中断处理测试
#[embedded_test(enable_interrupts = true)]
fn test_timer_interrupt() {
static INTERRUPT_FIRED: AtomicBool = AtomicBool::new(false);
let timer = Timer::new();
timer.set_interrupt_handler(|| INTERRUPT_FIRED.store(true, Ordering::SeqCst));
timer.start(100);
while !INTERRUPT_FIRED.load(Ordering::SeqCst) {}
assert!(true, "定时器中断未触发");
}
3. 多线程测试
#[embedded_test(threads = 2)]
fn test_thread_safety() {
let shared = Mutex::new(0);
let thread1 = spawn(|| {
let mut guard = shared.lock();
*guard += 1;
});
let thread2 = spawn(|| {
let mut guard = shared.lock();
*guard += 1;
});
thread1.join();
thread2.join();
assert_eq!(*shared.lock(), 2, "线程安全测试失败");
}
与标准测试框架集成
embedded-test-macros
可以与标准的#[test]
属性一起使用:
#[test]
#[embedded_test]
fn hybrid_test() {
// 既可以在主机上运行的标准测试
// 也可以在目标硬件上运行的嵌入式测试
}
测试输出
库提供了嵌入式友好的输出格式:
[EMBEDDED TEST] test_adc_reading... OK (12ms)
[EMBEDDED TEST] test_pwm_output... FAIL (PWM输出不正确)
最佳实践
- 为每个硬件外设创建单独的测试模块
- 使用
setup
功能初始化复杂硬件配置 - 合理设置测试超时时间
- 将关键硬件验证测试标记为
#[critical_test]
- 结合
defmt
等日志框架增强测试输出
注意事项
- 确保测试代码不会影响实际应用逻辑
- 注意资源冲突问题
- 在真实硬件上运行前,尽可能在模拟器中验证测试
- 考虑添加硬件复位后的状态验证
完整示例代码
下面是一个完整的嵌入式测试示例,展示了如何测试一个嵌入式系统中的多个硬件组件:
#![no_std]
#![no_main]
use embedded_test_macros::embedded_test;
use cortex_m_rt::entry;
use panic_halt as _;
// 模拟硬件外设
struct Adc;
struct Pwm;
struct Sensor;
struct Uart;
impl Adc {
fn new() -> Self { Adc }
fn read_channel(&self, _ch: u8) -> u16 { 2048 } // 模拟ADC读数
}
impl Pwm {
fn new() -> Self { Pwm }
fn set_duty_cycle(&self, _percent: u8) {}
}
impl Sensor {
fn new() -> Self { Sensor }
fn query(&self) -> Result<(), ()> { Ok(()) }
}
impl Uart {
fn new() -> Self { Uart }
fn write(&self, _data: &[u8]) {}
}
// 硬件初始化函数
fn init_hardware() {
// 这里通常是硬件初始化代码
}
#[entry]
fn main() -> ! {
loop {}
}
// ADC测试
#[embedded_test]
fn test_adc_basic() {
let adc = Adc::new();
let value = adc.read_channel(0);
assert_within_range!(value, 0..=4095, "ADC值超出有效范围");
}
// PWM测试需要硬件初始化
#[embedded_test(setup = init_hardware)]
fn test_pwm_output() {
let pwm = Pwm::new();
pwm.set_duty_cycle(50);
// 这里通常会检查实际硬件输出
assert!(true, "PWM测试通过");
}
// 传感器测试带超时
#[embedded_test(timeout_ms = 200)]
fn test_sensor_communication() {
let sensor = Sensor::new();
let result = sensor.query();
assert!(result.is_ok(), "传感器通信失败");
}
// 复杂测试:UART传输
#[embedded_test]
fn test_uart_loopback() {
let uart = Uart::new();
let test_data = b"test";
uart.write(test_data);
// 这里通常会验证实际接收到的数据
assert!(true, "UART回环测试通过");
}
// 关键测试标记
#[embedded_test]
#[critical_test]
fn test_system_clock() {
// 测试系统时钟是否正常运行
assert!(true, "系统时钟测试通过");
}
这个完整示例展示了:
- 基本的ADC测试
- 需要硬件初始化的PWM测试
- 带超时的传感器测试
- UART通信测试
- 标记为关键测试的系统时钟测试
所有测试都可以在嵌入式目标上运行,同时也支持在主机上运行的标准测试框架。