2014/02: 嵌入式測試驅動開發

May 7, 2018 | Author: Anonymous | Category: Documents
Report this link


Description

1. 嵌入式測試驅動開發 Hugo 2/13/2014 2. 我們熟悉的開發方式 3. 先寫程式,再寫測試找 bug Test-After Development (TAD) 4. 問題是 ... 沒出錯不知道哪裡有 bug 5. bug 小時候放著不管 6. 長大很恐怖 7. 有什麼方法 ... 能盡早把 bug 找出來 ? 8. 先寫測試找 bug ,再寫程式 Test-Driven Development (TDD) 9. Test Driven Development 測試驅動開發 10. TDD 循環 • 紅燈:增加一個運行失敗, 甚至無法編譯的測試。 • 綠燈:快速修改,只做能讓 測試通過的工作。 • 黃燈:重構,移除重複改進 代碼可讀性。 《 Test Driven Development: By Example 》 11. 測試 是 TDD 的關鍵 12. 建立 建立 Setup Setup 運行 運行 Exercise Exercise 驗證 驗證 Verify Verify 單元測試四階段 拆卸 拆卸 Teardown Teardown 13. 自動化單元測試框架 測試案例 #1 測試案例 #2 ... 測試案例 #1 測試案例 #2 report 測試結果 #1 測試結果 #1 測試框架 測試框架 測試結果 #2 測試結果 #2 產品代碼 ((受測代碼 )) 產品代碼 受測代碼 ... proj/ fun.c objs/ projTest/ src/ func.o src/ funTest.c objs/ func.o funcTest.o 目標環境 測試環境 14. TDD 的好處 • • • • • • 產生 bug 更少 除錯時間更短 不會說謊的文件 改善設計 監督進度 內心平靜 15. 嵌入式測試驅動開發 有什麼特別的地方嗎 ? 16. 依賴硬體,浪費時間 17. Code Code 單元測試 單元測試 運行系統 運行系統 開發環境 開發環境 目標環境 目標環境 雙目標開發 - 解開硬體的依賴 18. 嵌入式 TDD 循環 階段一 階段二 階段三 階段四 階段五 開發環境 TDD 交叉編譯 相容測試 評估版 單元測試 目標硬體 單元測試 目標硬體 驗收測試 很頻繁 不頻繁 《 Test-Driven Development for Embedded C 》 19. 嵌入式驅動開發工具 • Unity – C 語言自動化測試框架 – 用 Ruby Script 安裝測 試 • CppUTest – C/C++ 自動化測試框架 – 用 Ruby Script 將測試 轉換成 Unity 測試 Unity http://throwtheswitch.org/, CppUTest http://cpputest.github.io/ 20. 範例 - 開發 LED 驅動程式 21. 這玩意我 10 行程式就搞定了 22. 這麼簡單還需要測試嗎 ? MyLedDriver.c MyLedDriver.c #define LED_REGISTER 0x80001234 #define LED_REGISTER 0x80001234 void LedDriver_Set (uint16_t value) void LedDriver_Set (uint16_t value) { { *((uint16_t *)LED_REGISTER) = value; *((uint16_t *)LED_REGISTER) = value; } } uint16_t LedDriver_Get (void) uint16_t LedDriver_Get (void) { { return *((uint16_t *)LED_REGISTER); return *((uint16_t *)LED_REGISTER); } } LedUser.c LedUser.c void TurnOnLed8 (void) void TurnOnLed8 (void) { { LedDriver_Set (1 minuteOfDay = theMinute; } } FakeTimeServiceTest.c FakeTimeServiceTest.c TEST (FakeTimeService, Set) TEST (FakeTimeService, Set) { { Time time; Time time; } } FakeTimeService_SetMinute (42); FakeTimeService_SetMinute (42); TimeService_GetTime (&time); TimeService_GetTime (&time); LONGS_EQUAL (42, time.minuteOfDay); LONGS_EQUAL (42, time.minuteOfDay); 44. Test Spy 45. FakeTimeService.c FakeTimeService.c static int theMinute; static int theMinute; int FakeTimeService_GetMinute (void) int FakeTimeService_GetMinute (void) { { return theMinute; return theMinute; } } void TimeService_SetDay (Time* time) void TimeService_SetDay (Time* time) { { theMinute = time->minuteOfDay; theMinute = time->minuteOfDay; } } FakeTimeServiceTest.c FakeTimeServiceTest.c TEST (FakeTimeService, Get) TEST (FakeTimeService, Get) { { Time time; Time time; } } time->minuteOfDay = 42; time->minuteOfDay = 42; TimeService_SetTime (&time); TimeService_SetTime (&time); LONGS_EQUAL (42, FakeTimeService_GetMinute()); LONGS_EQUAL (42, FakeTimeService_GetMinute()); 46. Mock Object 47. Flash Program 序列圖 - 成功的情形 48. FlashTest.c FlashTest.c TEST (Flash, WriteSucceeds) TEST (Flash, WriteSucceeds) { { int result = 0; int result = 0; MockIO_Expect_Write (0, 0x40); MockIO_Expect_Write (0, 0x40); MockIO_Expect_Write (0x1000, 0xBEEF); MockIO_Expect_Write (0x1000, 0xBEEF); MockIO_Expect_ReadThenReturn (0, 1GetTime = myGetTime; return (Watch*)self; return (Watch*)self; } } 《 interface 》 《 interface 》 Watch Watch Digital Digital Watch Watch 53. 用 C 語言實現類別多型 User.c User.c void doSetTime (Watch* watch, Time time) { void doSetTime (Watch* watch, Time time) { watch->SetTime (time); watch->SetTime (time); } } DigitalWatch.c DigitalWatch.c static void mySetTime (Watch* watch, Time time) static void mySetTime (Watch* watch, Time time) { { DigitalWatch* self = (DigitalWatch*)watch; DigitalWatch* self = (DigitalWatch*)watch; self->time = time; self->time = time; } } MechanicWatch.c MechanicWatch.c static void mySetTime (Watch* watch, Time time) static void mySetTime (Watch* watch, Time time) { { MechanicWatch* self = (MechanicWatch*)watch; MechanicWatch* self = (MechanicWatch*)watch; self->time = time; self->time = time; } } User User 《 interface 》 《 interface 》 Watch Watch Digital Digital Watch Watch Mechanic Mechanic Watch Watch 54. SOLID 設計原則 User User 《 interface 》 《 interface 》 Watch Watch Digital Digital Watch Watch Mechanic Mechanic Watch Watch Pocket Pocket Watch Watch SRP 單一職責 OCP 開放封閉 LSP 替換原則 ISP 介面分離 DIP 依賴倒轉 《 Agile Software Development, Principle, Patterns, and Practices 》 55. 隨著代碼不斷增加 系統架構變得越來越混亂 56. 代碼的壞味道 • • • • • • • • • • • 重複代碼 壞名字 義大利麵式代碼 長函式 眼花撩亂的布林運算 重複的 switch/case 邪惡的嵌套 依戀情結 參數太多 注釋、注釋掉的代碼 條件編譯 57. CodeSmells.c CodeSmells.c void foobar (Time* time, Work* work) void foobar (Time* time, Work* work) { { if (work->item != NULL) { if (work->item != NULL) { Day day = time->dayOfWeek; Day day = time->dayOfWeek; int min = time->minuteOfDay; int min = time->minuteOfDay; if ((day >= MONDAY && day = MONDAY && day = 9*60 && min = 9*60 && min = 13*60 && min = 13*60 && min type == CODING) if (work->type == CODING) doCode (work->item); doCode (work->item); else else doDebug (work->item); doDebug (work->item); // doSomethingImportant (); // doSomethingImportant (); } } } } } } 58. 重構是 “在不改變當前外部行為的條件下, 對現有代碼進行修改的過程。” 《 Refactoring: Improving the Design of Existing Code 》 59. RefactoredCode.c RefactoredCode.c static bool static bool { { ... ... } } static void static void { { ... ... } } isWorkTime (Time* time) isWorkTime (Time* time) workHard (Work* work) workHard (Work* work) void workInOffice (Time* time, Work* work) void workInOffice (Time* time, Work* work) { { if (!isWorkTime(time)) if (!isWorkTime(time)) return; return; } } workHard (work); workHard (work); 60. 唉呦 不錯 重構這個屌 61. 但實際動手 你肯定會講一句話 ... 62. 砍掉重練比較快 63. 在真實世界裡 我們必須要跟遺留代碼戰鬥 64. 遺留代碼 = 沒有測試的代碼 65. 修改遺留代碼 • • • • • 發現改動點 找到測試點 斷開依賴 寫測試 改動和重構 《 Working Efficiently with Legacy Code 》 66. 測試點 • 接縫 ( 函式呼叫 ) • 全域變數 / 感知變 量 • 除錯輸出 • 嵌入監控 67. 把遺留代碼放到測試框架中 TestLegacyCode.c TestLegacyCode.c void addNewLegacyCTest() void addNewLegacyCTest() { { makeItCompile(); makeItCompile(); makeItLink(); makeItLink(); while (runCrashes()) { while (runCrashes()) { findRuntimeDependency(); findRuntimeDependency(); fixRuntimeDependency(); fixRuntimeDependency(); } } addMoreLegacyCTests(); addMoreLegacyCTests(); } } 68. TDD 聽來不錯,但是 ... 69. 不知怎麼開始 ? 70. 先來場 Coding Dojo 吧 71. 參考資料 《 《 《 《 《 《 《 Test-Driven Development for Embedded C 》 Test Driven Development: By Example 》 xUnit Test Patterns: Refactoring Test Code 》 Refactoring: Improving the Design of Existing Code 》 Working Efficiently with Legacy Code 》 Agile Software Development, Principle, Patterns, and Practices 》 Design Patterns for Embedded Systems in C: An Embedded Software Engineering Toolkit 》


Comments

Copyright © 2025 UPDOCS Inc.