星標公眾號,讓嵌入式知識 “投喂” 不停歇!
作為嵌入式開發(fā)者,你是否經(jīng)常遇到這樣的情況:接口設計不合理,導致后期大量重構;需求變更后,代碼改得面目全非...
今天我們要聊的測試驅動開發(fā)(TDD),無論你使用C、C++還是Rust開發(fā)嵌入式系統(tǒng),TDD都能讓你的代碼更可靠、接口更清晰。
TDD的核心是一個簡單但強大的循環(huán):紅 - 綠 - 重構

與傳統(tǒng)“先寫代碼再補測試”不同,TDD要求測試先行。
注意:我們不是測試寫完就結束,也不是追求100%覆蓋率,而是用測試來驅動設計。
TDD帶來的好處:

假設有一個嵌入式需求:實現(xiàn)一個LED驅動器,可以控制LED的開關和閃爍頻率。

TDD思路:先寫測試,描述期望行為
// test_led_driver.c
voidtest_led_turn_on(void){
led_init();
led_on();
TEST_ASSERT_TRUE(is_led_on());
}
voidtest_led_blink_frequency(void){
led_init();
led_set_blink_freq(5); // 5Hz
led_blink_start();
// 驗證實際閃爍頻率為5Hz
TEST_ASSERT_EQUAL(5, get_actual_blink_freq());
}
通過寫測試,我們實際上在定義接口:
led_init()初始化led_on()/led_off()控制狀態(tài)led_set_blink_freq()設置頻率led_blink_start()啟動閃爍TDD天然驅動出高內(nèi)聚、低耦合的接口。

繼續(xù)LED例子:
第一版測試只關注基本功能,接口可能簡單。但當我們寫第二個測試時,會發(fā)現(xiàn)設計問題:
voidtest_led_state_after_init(void){
led_init();
TEST_ASSERT_FALSE(is_led_on()); // 初始應為關閉
}
這個測試揭示了:led_init()需要明確設置初始狀態(tài)。
第三個測試可能發(fā)現(xiàn)新需求:多個LED實例。
voidtest_multiple_leds(void){
led_t led1 = led_create(GPIO_PIN_1);
led_t led2 = led_create(GPIO_PIN_2);
led_on(led1);
TEST_ASSERT_TRUE(is_led_on(led1));
TEST_ASSERT_FALSE(is_led_on(led2));
}
于是接口從全局函數(shù)演變?yōu)?/span>句柄式接口,更清晰、可擴展。
嵌入式開發(fā)有幾個特殊難點:
問題:測試無法在真實硬件上運行
解決:使用HAL(硬件抽象層) + Mock。
一個可自動生成嵌入式Mock模塊的工具!
一個可應用于嵌入式的輕量級單元測試框架!
// hal_gpio.h - 硬件抽象
voidhal_gpio_write(uint32_t pin, bool level);
// test_led.c - 使用Mock
voidtest_led_on_sets_pin_high(void){
mock_hal_gpio_write_expect(GPIO_LED, true);
led_on();
}
問題:閃爍、延時等時序難以在單元測試中驗證
解決:抽象時間服務
// timer_service.h
voidtimer_delay_ms(uint32_t ms);
uint32_ttimer_get_ms(void);
// 測試時可以Mock時間
voidtest_blink_toggles_every_500ms(void){
led_blink_start(500);
timer_mock_advance(499);
TEST_ASSERT_FALSE(led_state_changed());
timer_mock_advance(1);
TEST_ASSERT_TRUE(led_state_changed());
}
問題:中斷服務程序難以測試
解決:將中斷邏輯提取為可測試函數(shù)
// 中斷只做最簡單的標志設置
voidEXTI0_IRQHandler(void){
button_interrupt_flag = true;
}
// 業(yè)務邏輯在主循環(huán)測試
voidtest_button_press_triggers_action(void){
button_interrupt_flag = true;
process_button_events(); // 可測試的函數(shù)
TEST_ASSERT_TRUE(action_executed());
}
測試驅動開發(fā)對于嵌入式開發(fā),它提供了一條從需求到可靠代碼的清晰路徑。
開始可能會覺得“先寫測試”很別扭,但堅持幾周,你會發(fā)現(xiàn)自己設計的接口更清爽,調(diào)試時間大幅減少,對代碼質(zhì)量更有信心。
猜你喜歡:
適用于嵌入式的輕量級環(huán)緩沖區(qū)管理庫!
Git 交互式變基修改commit描述
單例模式:嵌入式全局狀態(tài)一致性的守護者
嵌入式領域:Linux 與 RTOS 的巔峰對決!
嵌入式軟件進階指南,一起來進階!