2014/02: 嵌入式測試驅動開發
DESCRIPTION
TRANSCRIPT
![Page 1: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/1.jpg)
嵌入式測試驅動開發Hugo
2/13/2014
![Page 2: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/2.jpg)
我們熟悉的開發方式
![Page 3: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/3.jpg)
先寫程式,再寫測試找 bug
Test-After Development (TAD)
![Page 4: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/4.jpg)
問題是 ...沒出錯不知道哪裡有 bug
![Page 5: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/5.jpg)
bug 小時候放著不管
![Page 6: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/6.jpg)
長大很恐怖
![Page 7: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/7.jpg)
有什麼方法 ...能盡早把 bug 找出來 ?
![Page 8: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/8.jpg)
先寫測試找 bug ,再寫程式
Test-Driven Development (TDD)
![Page 9: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/9.jpg)
TestDrivenDevelopment測試驅動開發
![Page 10: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/10.jpg)
TDD 循環
• 紅燈:增加一個運行失敗,甚至無法編譯的測試。
• 綠燈:快速修改,只做能讓測試通過的工作。
• 黃燈:重構,移除重複改進代碼可讀性。
《 Test Driven Development: By Example》
![Page 11: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/11.jpg)
測試 是 TDD 的關鍵
![Page 12: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/12.jpg)
建立Setup建立
Setup運行
Exercise運行
Exercise驗證
Verify驗證
Verify拆卸
Teardown拆卸
Teardown
單元測試四階段
![Page 13: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/13.jpg)
自動化單元測試框架
測試框架測試框架
測試案例 #1測試案例 #1 測試案例 #2測試案例 #2 ...
產品代碼 ( 受測代碼 )產品代碼 ( 受測代碼 )
report 測試結果 #1測試結果 #1
測試結果 #2測試結果 #2
...
proj/ src/objs/ func.o
fun.c
projTest/ src/ funTest.cobjs/ func.o
funcTest.o
目標環境
測試環境
![Page 14: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/14.jpg)
TDD 的好處• 產生 bug 更少• 除錯時間更短• 不會說謊的文件• 改善設計• 監督進度• 內心平靜
![Page 15: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/15.jpg)
嵌入式測試驅動開發有什麼特別的地方嗎 ?
![Page 16: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/16.jpg)
依賴硬體,浪費時間
![Page 17: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/17.jpg)
雙目標開發 - 解開硬體的依賴
CodeCode
單元測試單元測試 運行系統運行系統
開發環境開發環境 目標環境目標環境
![Page 18: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/18.jpg)
嵌入式 TDD 循環
《 Test-Driven Development for Embedded C》
階段一 階段二 階段三 階段四 階段五
開發環境TDD
交叉編譯相容測試
評估版單元測試
目標硬體單元測試
目標硬體驗收測試
很頻繁 不頻繁
![Page 19: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/19.jpg)
嵌入式驅動開發工具
• Unity– C 語言自動化測試框架– 用 Ruby Script 安裝測
試• CppUTest
– C/C++ 自動化測試框架– 用 Ruby Script 將測試
轉換成 Unity 測試
Unity http://throwtheswitch.org/, CppUTest http://cpputest.github.io/
![Page 20: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/20.jpg)
範例 - 開發 LED 驅動程式
![Page 21: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/21.jpg)
這玩意我 10 行程式就搞定了
![Page 22: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/22.jpg)
這麼簡單還需要測試嗎 ?MyLedDriver.cMyLedDriver.c
#define LED_REGISTER 0x80001234void LedDriver_Set (uint16_t value){ *((uint16_t *)LED_REGISTER) = value;}uint16_t LedDriver_Get (void){ return *((uint16_t *)LED_REGISTER);}
#define LED_REGISTER 0x80001234void LedDriver_Set (uint16_t value){ *((uint16_t *)LED_REGISTER) = value;}uint16_t LedDriver_Get (void){ return *((uint16_t *)LED_REGISTER);}
LedUser.cLedUser.c
void TurnOnLed8 (void){ LedDriver_Set (1 << 8);}
void TurnOnLed8 (void){ LedDriver_Set (1 << 8);}
![Page 23: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/23.jpg)
沒有測試把關再怎麼簡單都可能出錯
![Page 24: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/24.jpg)
TDD 會怎麼做 ?
![Page 25: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/25.jpg)
測試列表
![Page 26: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/26.jpg)
先寫出測試失敗的測試LedDriverTest.cLedDriverTest.c
TEST (LedDriver, LedsOffAfterCreate){ uint16_t virtualLeds = 0xffff; LedDriver_Create (&virtualLeds); TEST_ASSERT_EQUAL (0, virtualLeds);}
TEST (LedDriver, LedsOffAfterCreate){ uint16_t virtualLeds = 0xffff; LedDriver_Create (&virtualLeds); TEST_ASSERT_EQUAL (0, virtualLeds);}
LedDriver.cLedDriver.c
void LedDriver_Create (uint16_t* address){}
void LedDriver_Create (uint16_t* address){}
Dependence Injection ( 依賴注入 )
![Page 27: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/27.jpg)
用最簡單的方式讓測試通過LedDriver.cLedDriver.c
void LedDriver_Create (uint16_t* address){ *address = 0;}
void LedDriver_Create (uint16_t* address){ *address = 0;}
$ makecompiling LedDriver.cLinking LedDirver_testsRunning LedDriver_tests.OK (1 tests, 1 ran, 1 checks, 0 ignored)
![Page 28: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/28.jpg)
再增加一個測試LedDriverTest.cLedDriverTest.c
TEST (LedDriver, TurnOnLedOne){ uint16_t virtualLeds; LedDriver_Create (&virtualLeds); LedDriver_TurnOn (1); TEST_ASSERT_EQUAL (1, virtualLeds);}
TEST (LedDriver, TurnOnLedOne){ uint16_t virtualLeds; LedDriver_Create (&virtualLeds); LedDriver_TurnOn (1); TEST_ASSERT_EQUAL (1, virtualLeds);}
LedDriver.cLedDriver.c
void LedDriver_TurnOn (int ledNumber){}
void LedDriver_TurnOn (int ledNumber){}
![Page 29: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/29.jpg)
寫 hardcode 讓測試通過LedDriver.cLedDriver.c
static uint16_t* ledsAddress;
void LedDriver_Create (uint16_t* address){ ledsAddress = address; *ledsAddress = 0;}
void LedDriver_TurnOn (int ledNumber){ *ledDriver = 1;}
static uint16_t* ledsAddress;
void LedDriver_Create (uint16_t* address){ ledsAddress = address; *ledsAddress = 0;}
void LedDriver_TurnOn (int ledNumber){ *ledDriver = 1;}
Data Encapsulation ( 資料封裝 )
![Page 30: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/30.jpg)
等等 ! 這不科學啊 ...hardcode 的實作有問題
![Page 31: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/31.jpg)
增加非當下測試所需的代碼會降低捕捉各種 bug 的動力
![Page 32: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/32.jpg)
先仿冒再建造保持小而專注的測試
![Page 33: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/33.jpg)
TDD 像是過河的墊腳石
![Page 34: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/34.jpg)
喔 ... 這就是 TDD 嗎 ?
![Page 35: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/35.jpg)
真正的系統長的像這樣
![Page 36: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/36.jpg)
依賴像一串肉粽
![Page 37: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/37.jpg)
控制輸入 & 監測輸出
直接輸入 直接輸出
間接輸入 間接輸出
![Page 38: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/38.jpg)
斷開魂結、斷開鎖鍊
![Page 39: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/39.jpg)
測試 替身
![Page 40: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/40.jpg)
何時使用測試替身 ?•獨立於硬體• 注入難以產生的輸入• 加速緩慢的合作者• 依賴不穩定的事情•取代未被實現的服務•對於難以配置的事物的依賴
![Page 41: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/41.jpg)
測試替身的替換技術• 編譯時期,透過 Preprocessor 替換•連結時期,透過 Object File 替換•執行時期,透過 Function Pointer 替換
![Page 42: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/42.jpg)
Test Stub
《 xUnit Test Patterns: Refactoring Test Code》
![Page 43: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/43.jpg)
FakeTimeService.cFakeTimeService.c
static int theMinute;
void FakeTimeService_SetMinute (int minute){ theMinute = minute;}void TimeService_GetTime (Time* time){ time->minuteOfDay = theMinute;}
static int theMinute;
void FakeTimeService_SetMinute (int minute){ theMinute = minute;}void TimeService_GetTime (Time* time){ time->minuteOfDay = theMinute;}
FakeTimeServiceTest.cFakeTimeServiceTest.c
TEST (FakeTimeService, Set){ Time time;
FakeTimeService_SetMinute (42); TimeService_GetTime (&time); LONGS_EQUAL (42, time.minuteOfDay);}
TEST (FakeTimeService, Set){ Time time;
FakeTimeService_SetMinute (42); TimeService_GetTime (&time); LONGS_EQUAL (42, time.minuteOfDay);}
![Page 44: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/44.jpg)
Test Spy
![Page 45: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/45.jpg)
FakeTimeService.cFakeTimeService.c
static int theMinute;
int FakeTimeService_GetMinute (void){ return theMinute;}void TimeService_SetDay (Time* time){ theMinute = time->minuteOfDay;}
static int theMinute;
int FakeTimeService_GetMinute (void){ return theMinute;}void TimeService_SetDay (Time* time){ theMinute = time->minuteOfDay;}
FakeTimeServiceTest.cFakeTimeServiceTest.c
TEST (FakeTimeService, Get){ Time time;
time->minuteOfDay = 42; TimeService_SetTime (&time); LONGS_EQUAL (42, FakeTimeService_GetMinute());}
TEST (FakeTimeService, Get){ Time time;
time->minuteOfDay = 42; TimeService_SetTime (&time); LONGS_EQUAL (42, FakeTimeService_GetMinute());}
![Page 46: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/46.jpg)
Mock Object
![Page 47: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/47.jpg)
Flash Program 序列圖 - 成功的情形
![Page 48: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/48.jpg)
FlashTest.cFlashTest.c
TEST (Flash, WriteSucceeds){ int result = 0;
MockIO_Expect_Write (0, 0x40); MockIO_Expect_Write (0x1000, 0xBEEF); MockIO_Expect_ReadThenReturn (0, 1<<7); MockIo_Expect_ReadThenReturn (0, 1<<7); MockIO_Expect_ReadThenReturn (0x1000, 0xBEEF);
Result = Flash_Write (0x1000, 0xBEEF);
LONG_EQUAL (0, result); MockIO_Verify_Complete();}
TEST (Flash, WriteSucceeds){ int result = 0;
MockIO_Expect_Write (0, 0x40); MockIO_Expect_Write (0x1000, 0xBEEF); MockIO_Expect_ReadThenReturn (0, 1<<7); MockIo_Expect_ReadThenReturn (0, 1<<7); MockIO_Expect_ReadThenReturn (0x1000, 0xBEEF);
Result = Flash_Write (0x1000, 0xBEEF);
LONG_EQUAL (0, result); MockIO_Verify_Complete();}
CMock http://throwtheswitch.org/, CppUMock http://cpputest.github.io/
![Page 49: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/49.jpg)
怎樣才算好設計 ?可測性、可讀性、可維護性
![Page 50: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/50.jpg)
借用物件導向的設計原則
![Page 51: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/51.jpg)
用 C 語言實現資料封裝
WallClock.hWallClock.h
void WallClock_SetTime (Time time);Time WallClock_GetTime (void);void WallClock_SetTime (Time time);Time WallClock_GetTime (void);
Watch.hWatch.h
typedef struct WatchStruct Watch;void SetTime (Watch* watch, Time time);Time GetTime (Watch* watch);
typedef struct WatchStruct Watch;void SetTime (Watch* watch, Time time);Time GetTime (Watch* watch);
![Page 52: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/52.jpg)
《 interface》Watch
《 interface》Watch
DigitalWatch
DigitalWatch
Watch.hWatch.h
typedef struct WatchStruct { void (*SetTime) (Watch*, Time); Time (*GetTime) (Watch*);} Watch;
typedef struct WatchStruct { void (*SetTime) (Watch*, Time); Time (*GetTime) (Watch*);} Watch;
DigitalWatch.cDigitalWatch.c
typedef struct DigitalWatchStruct { Watch* base; Time time;} DigitalWatch;
Watch* DigitalWatch_Create (void) { DigitalWatch* self = malloc(sizeof(DigitalWatch)); self->base->SetTime = mySetTime; self->base->GetTime = myGetTime; return (Watch*)self;}
typedef struct DigitalWatchStruct { Watch* base; Time time;} DigitalWatch;
Watch* DigitalWatch_Create (void) { DigitalWatch* self = malloc(sizeof(DigitalWatch)); self->base->SetTime = mySetTime; self->base->GetTime = myGetTime; return (Watch*)self;}
用 C 語言實現類別繼承
![Page 53: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/53.jpg)
《 interface》Watch
《 interface》Watch
DigitalWatch
DigitalWatch
MechanicWatch
MechanicWatch
DigitalWatch.cDigitalWatch.c
static void mySetTime (Watch* watch, Time time){ DigitalWatch* self = (DigitalWatch*)watch; self->time = time;}
static void mySetTime (Watch* watch, Time time){ DigitalWatch* self = (DigitalWatch*)watch; self->time = time;}
MechanicWatch.cMechanicWatch.c
static void mySetTime (Watch* watch, Time time){ MechanicWatch* self = (MechanicWatch*)watch; self->time = time;}
static void mySetTime (Watch* watch, Time time){ MechanicWatch* self = (MechanicWatch*)watch; self->time = time;}
用 C 語言實現類別多型User.cUser.c
void doSetTime (Watch* watch, Time time) { watch->SetTime (time);}
void doSetTime (Watch* watch, Time time) { watch->SetTime (time);}
UserUser
![Page 54: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/54.jpg)
《 interface》Watch
《 interface》Watch
DigitalWatch
DigitalWatch
MechanicWatch
MechanicWatch
UserUser
SOLID 設計原則SRP 單一職責OCP 開放封閉LSP 替換原則ISP 介面分離DIP 依賴倒轉
《 Agile Software Development, Principle, Patterns, and Practices》
PocketWatch
PocketWatch
![Page 55: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/55.jpg)
隨著代碼不斷增加系統架構變得越來越混亂
![Page 56: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/56.jpg)
代碼的壞味道• 重複代碼• 壞名字• 義大利麵式代碼• 長函式• 眼花撩亂的布林運算• 重複的 switch/case• 邪惡的嵌套• 依戀情結• 參數太多• 注釋、注釋掉的代碼• 條件編譯
![Page 57: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/57.jpg)
CodeSmells.cCodeSmells.c
void foobar (Time* time, Work* work){ if (work->item != NULL) { Day day = time->dayOfWeek; int min = time->minuteOfDay; if ((day >= MONDAY && day <= FRIDAY) && ((min >= 9*60 && min <= 12*60) || (min >= 13*60 && min <= 18*60)) { if (work->type == CODING) doCode (work->item); else doDebug (work->item); // doSomethingImportant (); } }}
void foobar (Time* time, Work* work){ if (work->item != NULL) { Day day = time->dayOfWeek; int min = time->minuteOfDay; if ((day >= MONDAY && day <= FRIDAY) && ((min >= 9*60 && min <= 12*60) || (min >= 13*60 && min <= 18*60)) { if (work->type == CODING) doCode (work->item); else doDebug (work->item); // doSomethingImportant (); } }}
![Page 58: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/58.jpg)
重構是“在不改變當前外部行為的條件下,對現有代碼進行修改的過程。”
《 Refactoring: Improving the Design of Existing Code》
![Page 59: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/59.jpg)
RefactoredCode.cRefactoredCode.c
static bool isWorkTime (Time* time){ ...}static void workHard (Work* work){ ...}
void workInOffice (Time* time, Work* work){ if (!isWorkTime(time)) return;
workHard (work);}
static bool isWorkTime (Time* time){ ...}static void workHard (Work* work){ ...}
void workInOffice (Time* time, Work* work){ if (!isWorkTime(time)) return;
workHard (work);}
![Page 60: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/60.jpg)
唉呦 不錯 重構這個屌
![Page 61: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/61.jpg)
但實際動手你肯定會講一句話 ...
![Page 62: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/62.jpg)
砍掉重練比較快
![Page 63: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/63.jpg)
在真實世界裡我們必須要跟遺留代碼戰鬥
![Page 64: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/64.jpg)
遺留代碼 = 沒有測試的代碼
![Page 65: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/65.jpg)
修改遺留代碼• 發現改動點• 找到測試點• 斷開依賴• 寫測試• 改動和重構
《Working Efficiently with Legacy Code》
![Page 66: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/66.jpg)
測試點•接縫 (函式呼叫 )•全域變數 /感知變量
• 除錯輸出• 嵌入監控
![Page 67: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/67.jpg)
把遺留代碼放到測試框架中
TestLegacyCode.cTestLegacyCode.c
void addNewLegacyCTest(){ makeItCompile(); makeItLink(); while (runCrashes()) { findRuntimeDependency(); fixRuntimeDependency(); } addMoreLegacyCTests();}
void addNewLegacyCTest(){ makeItCompile(); makeItLink(); while (runCrashes()) { findRuntimeDependency(); fixRuntimeDependency(); } addMoreLegacyCTests();}
![Page 68: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/68.jpg)
TDD 聽來不錯,但是 ...
![Page 69: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/69.jpg)
不知怎麼開始 ?
![Page 70: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/70.jpg)
先來場 Coding Dojo 吧
![Page 71: 2014/02: 嵌入式測試驅動開發](https://reader033.vdocuments.net/reader033/viewer/2022061201/5478e7715906b571048b4613/html5/thumbnails/71.jpg)
參考資料《 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 Soft
ware Engineering Toolkit 》