Test-Driven Development for Embedded C
by James W Grenning
Twitterのアカウントはこちら@jwgrenning
linkedinのアカウントはこちらwww.linkedin.com/in/jwgrenning
(メッセージに今日の研修で知り合ったことを書いてください)
1Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
付箋を2枚書いて下さいあなたの開発時間に占めるテストとデバッグの割合は?
プログラマが考えた通りにコードが動くことを確かめるのは誰の仕事か?
コーディング % テスティング %+ デバッギング % 100%
顧客
2
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
付箋を2枚書いて下さい開発の何が好き?
... が好き ... が苦手
開発で一番苦手な活動は?
3Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
TDDには色々な要素がある
4
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
TDDのいくつかの部分について見ていこう
実際はもっとたくさんある
5Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
私が答える質問
• TDDとは何か?•どのように依存関係のあるコードをテストするか?
•どのようにTDDを組み込み開発に適用するか?
6
Copyright © 2008-20132013 James W. GrenningAll Rights Reserved. For use by training attendees.
知識
理解
スキル
熟練• 質問する• 経験を得る• スキルを築く
“間違いたくなければ質問するな。決して正しくなりたくなければ質問するな”
ガブリエル・ベネフィールド
7Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
• テストを書く• ビルドされないことを確認する• ビルドするが失敗する• ビルドを通す• リファクタリングする (汚いコードを綺麗にする)
• 完了まで繰り返す
TDD
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
開発とテストを連続させ、バグを防ぐ
開発
テスト
9Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
グループ演習
10
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
Circular Buffer(循環バッファ)モジュールを作る
41 59 14 33 7 31
In-Index Out-Index
Out-Index In-Index
23 7 66 12 99 16 90
Out-Index In-Index
42 -6 23 7 66 12 99 16 90
Out-Index In-Index
99
11Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
ニーズ
• Circular Buffer は整数の配列を持つ• Circular Buffer の使用と完全性を管理するモジュールを作成する
12
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
テスト駆動開発を始める前に
• API はどうあるべきか?–関数に名前をつける
• 初期データ構造はどうあるべきか?‒あなたが使う分野を特定する
• どうテストするか?–数行で表現する
• どんなテストケースが必要か?‒リストを作る
13Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
分析しすぎに注意
アイディアがたくさん湧く
分析しすぎIdeas
Time
推測の上に推測を重ねている考えすぎ
行動に移るタイミング
14
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
グループ演習に対する私の解答
演習が終わるまでこの先の資料を見ないこと
15Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
インタフェースに対するアイディアを考える
+ Put+ Get+ IsEmpty+ IsFull//etc
CircularBuffer
16
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
どのように実装するか?(ただし、分析し過ぎに注意)
23 7 66 12 99 16 90 99
in-index
out-index
length
buffer pointer
...
Text
17Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
テストリストを作る(分析し過ぎに注意)
Circular Buffer Tests
Initially Empty Transition to empty Transition to Full Transition from Full Put-Get FIFO Put to full Get from empty Filled but not wrapped around Wrap around
Circular Bufferのテスト
最初は空空に遷移する満杯に遷移する満杯から遷移するFIFOの入出力空の時に取得する満杯だが一周しない一周する
18
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
C/C++でのTDDステートマシン(一度に1つの問題を解決する)
必要になればテストリストに新しいテストを追加する19
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
デモ
20
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
TEST(CircularBuffer, empty_after_creation){ CHECK_TRUE(CircularBuffer_IsEmpty(buffer));}
21Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
bool CircularBuffer_IsEmpty(CircularBuffer * self){ return true;}
すべてのテストをパスする最もシンプルな実装
22
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
TEST(CircularBuffer, empty_after_creation){ CHECK_TRUE(CircularBuffer_IsEmpty(buffer));}
TEST(CircularBuffer, not_empty_after_put){ CircularBuffer_Put(buffer, 42); CHECK_FALSE(CircularBuffer_IsEmpty(buffer));}
23Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
bool CircularBuffer_IsEmpty(CircularBuffer * self){ return self->index == 0;}
bool CircularBuffer_Put(CircularBuffer * self, int value){ self->index++ return true;}
すべてのテストをパスする最もシンプルな実装
24
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
TEST(CircularBuffer, empty_after_creation){ CHECK_TRUE(CircularBuffer_IsEmpty(buffer));}
TEST(CircularBuffer, not_empty_after_put){ CircularBuffer_Put(buffer, 42); CHECK_FALSE(CircularBuffer_IsEmpty(buffer));}
TEST(CircularBuffer, empty_after_removing_the_last_item){ CircularBuffer_Put(buffer, 42); CHECK_FALSE(CircularBuffer_IsEmpty(buffer)); CircularBuffer_Get(buffer); CHECK_TRUE(CircularBuffer_IsEmpty(buffer));}
25Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
bool CircularBuffer_IsEmpty(CircularBuffer * self){ return self->index == self->outputIndex;}
bool CircularBuffer_Put(CircularBuffer * self, int value){ self->index++ return true;}
int CircularBuffer_Get(CircularBuffer * self){ self->outputIndex++ return -1;}
すべてのテストをパスする最もシンプルな実装
26
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
コーディングは危険だから注意深くやろう
• TDDは一度に一つの問題を解決する
• 最初にテストにフォーカスし不完全なコードを実装する
• テストが完了したら、 コードはインテグレーションの準備が完了している
Photo by Andrew Bossi (Own work) [CC-BY-SA-2.5 (www.creativecommons.org/licenses/by-sa/2.5)], via Wikimedia Commons
27Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
動作する複雑なシステムは動作するシンプルなシステムから常に進化することが分かった
28
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
あなたのプログラムにはバグが埋め込まれる。そしてそれらが見つかった時、あなたは驚くだろう。
29Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
30
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
持続可能性
Speculate Code Test Debug伝統的な開発
開発者が気づいていない、あるいは計画していない時間
テスト駆動開発 Speculate Test and Code Debug
遅く感じる
検討
検討31
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
あとでデバッグ型プログラミングはジャグリングのようだ
32
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
ナイフを何本ジャグリングするのが好き?• TDDは一度に一つの問題を解決する
Photo by Andrew Bossi (Own work) [CC-BY-SA-2.5 (www.creativecommons.org/licenses/by-sa/2.5)], via Wikimedia Commons
33Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
•注意深いやり方が上手くなれば速く進める
注意深いやり方が近道だ!
34
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
組み込みソフトウェアへの適用
35Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
組み込みの何が特別なのか?
• ハードウェアとの並行開発•ハードウェア・ボトルネック•クロスコンパイル•制約のあるメモリとIO•ターゲット上でのデバッグ•アーキテクチャ
36
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
ハードウェアが足りない!
• まだ存在しない•他の誰かが使っている•ハードウェアにハグがある
37Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
38
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
39Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
40
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
を最小化しよう
Debug
on
Hardware
DoH!
Copyright Fox Broadcasting. Used under fair use.
41Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
TDDを組み込み開発に適用する
42
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
TDDを組み込み開発に適用する
参照: http://renaissancesoftware.net/files/articles/ProgressBeforeHardware.pdf
43Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
35
組み込みでの継続的インテグレーション3. CISはリポジトリの変更を検出すると、ローカルコピーを更新して、ビルドとホストベースのテストを実行する。
2. 開発者が定期的に成果物をチェックインする。
1. 開発者が全てのユニットテストを通し、成果物をチェックインする。
4.ホスト上のビルドとテストが成功すると、CISはビルドしたバイナリをTMSに渡す。TMSはそれをシミュレータ、評価ボード/リファレンスボード、ターゲットシステムなどにデプロイする。
SCR, CIS と TMS は必ずしも別のマシンである必要はない
DeveloperWorkstation
Source CodeRepository
Continuous IntegrationServer
Target System
Eval/ReferenceSystem
Simulator
Test ManagementSystem
44
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
適切なステージで問題を見つけるステージ ステージで見つかりそうな問題
1 ロジック、設計、モジュール性、インタフェース、境界条件
2 コンパイラの互換性(言語の機能)ライブラリの互換性(ヘッダーファイル、宣言)
3 プロセッサ実行の問題(コンパイラとライブラリのバグ)移植の問題(ワードサイズ、アライメント、エンディアン)
4 ステージ 3 と同じハードウェアインテグレーション問題ハードウェア仕様の誤解
5 ステージ 4 と同じ機能仕様の誤解
45Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
どのように依存関係のあるコードをテストするか?
46
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
コラボレータのあるモジュール• LightSchedulerはRTOSによって1分毎に起こされる
• ライトの1つがコントロールされる時間になれば、 LightControllerはそのライトをオン/オフするよう指示される
Time Service
+ GetTime()+ SetPeriodicAlarm()
LightScheduler
+ScheduleTurnOn()+RemoveSchedule()+WakeUp()
Light Controller
+ On(id)+ Off(id)
Hardware RTOS
<<anonymous callback>>
Admin Console
47Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
インターフェイスに対してプログラムする
•インターフェイスと実装を別々の構成要素に分離する
•この設計はうまく責務を分離している
<<interface>>Time Service
+ GetTime()+ SetPeriodicAlarm()
LightScheduler
+ ScheduleTurnOn()+ RemoveSchedule()+WakeUp()
<<interface>>Light Controller
+ On(id)+ Off(id)
Model 42 Hardware RTOS
<<anonymous callback>>
Model 42Light Controller
RTOS Time Service
<<implements>> <<implements>>
Admin Console
48
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
コラボレータのあるモジュールをテストする
• 可能ならば本物のコラボレータを使う
•フェイクは必要なときだけ使う
<<interface>>Time Service
+ GetTime()+ SetPeriodicAlarm()
LightScheduler
Test
LightScheduler
+ ScheduleTurnOn()+ RemoveSchedule()+wakeUp()
<<interface>>Light Controller
+ On(id)+ Off(id)
Light Controller Spy
FakeTime Service
<<implements>> <<implements>>
49Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
スケジューラをテストするTEST(LightScheduler, ScheduleOnTodayNotTimeYet){ LightScheduler_ScheduleTurnOn(3, EVERYDAY, 1000); FakeTimeSource_SetMinute(999);
LightScheduler_Wakeup();
LONGS_EQUAL(LIGHT_NA, LightControllerSpy_GetState(3));}
TEST(LightScheduler, ScheduleOnTodayItsTime){ LightScheduler_ScheduleTurnOn(3, EVERYDAY, 1000); FakeTimeSource_SetMinute(1000);
LightScheduler_Wakeup();
LONGS_EQUAL(LIGHT_ON, LightControllerSpy_GetState(3));}
50
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
部分的なスケルトンで早く設計やテストのアイデアを試す
51Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
テストダブル
52
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
テストダブル
• テストダブルはテストの間、プロダクトコードのある部分の代わりになる
•テストダブルはモジュールをユニットテスト可能にするテストフィクスチャの一部
53
タコのようなテスト依存関係
Test
Code Under Test
Depended On
ComponentDOC DOC
Transitively DOC TDOC TDOCTDOCTDOC
54
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
タコを管理する
Test
Code Under Test
Test Double Test Double Test Double
55Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
可能ならばプロダクションコードを使う必要な時だけテストダブルを使う
Test
Code Under Test
Depended On
ComponentTest Double Test Double
Transitively DOC TDOC
56
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
テストダブルの種類[XUNIT]
• 他の名前‒フェイク‒モックオブジェクト‒スパイ‒ヌルオブジェクト‒フェイク爆弾‒他にもある
57Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
Cによるテストダブルの選択肢
•リンク時置換‒ オブジェクトファイルやライブラリを置換する•実行時置換‒ 関数ポインタ‒ 関数ポインタテーブル•プリプロセッサ置換‒ 私は好きではないが、時々必要になる•動く一番シンプルな選択肢を使う
58
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
C++によるテストダブルの選択肢• リンク時置換‒オブジェクトファイルやライブラリを置換する• 実行時置換‒1つのクラスを別のものに置き換える‒仮想関数‒インタフェースクラス
‒ 1つの関数を別のものに置き換える‒C関数ポインタ
• プリプロセッサ置換‒ 私は好きではないが時々必要になる
59Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
ターゲットへの依存関係を管理する
60
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
Spaghetti Slide
Fair use for education: Thanks http://catalinpetrescu.webs.com/apps/photos/photo?photoid=5490225561Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
関心を分離する
• ハードウェアやOSの詳細から製品ロジックを分離する
• もしできないならオフターゲットでテストできない
• あなたのコードの寿命が短くなる
<<interface>>Time Service
+ GetTime()+ SetPeriodicAlarm()
LightScheduler
+ ScheduleTurnOn()+ RemoveSchedule()+WakeUp()
<<interface>>Light Controller
+ On(id)+ Off(id)
Model 42 Hardware RTOS
<<anonymous callback>>
Model 42Light Controller
RTOS Time Service
<<implements>> <<implements>>
Admin Console
62
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
問題例
• コンパイラベンダーが C を拡張する• 開発者が ‘asm’ を埋め込む• ヘッダファイルがベンダーロックインになる
63Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
コンパイラベンダーがC を拡張する
//snip.../* Address Mode Register */extern cregister volatile unsigned int AMR; //snip...
//snip...interrupt void one_ms_tic(void){ ...}//snip...
64
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
プリプロセッサでベンダー拡張を隠すため強制的にインクルードする
//hide_stuff.h
#define interrupt#define cregister
無効化するために hidestuff.h を強制的にインクルードする
参照: http://www.renaissancesoftware.net/blog/archives/249
65Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
開発者が ‘asm’ を埋め込むvoid foo(void){ do { asm("NOP"); asm("PLOP"); asm("READ 0"); status = IORead(0); } while ((status & ReadyBit) == 0);}
66
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
プリプロセッサテストダブル//nullasm.h
#define asm
asm を無効化するために nullasm.h を強制的にインクルードする
参照: http://www.renaissancesoftware.net/blog/archives/136
67Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
AsmSpy テストダブル
#define asm AsmSpy
void AsmSpy_Create(int size);void AsmSpy_Destroy(void);void AsmSpy(const char *);const char * AsmSpy_Debrief(void);
AsmSpy.h を強制的にインクルードすることで、すべての asm() 指令は AsmSpy() 呼び出しに置き換えられ、補足可能になる。参照: http://www.renaissancesoftware.net/blog/archives/136
68
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
asmのモニタ方法を示すAsmSpyのテスト
TEST(AsmSpy, CaptureAsmInstructions){ AsmSpy("NOP"); AsmSpy("FLOP"); AsmSpy("POP"); STRCMP_EQUAL("NOP;FLOP;POP;", AsmSpy_Debrief());}
asmへの指示を補足するため、AsmSpy.hを強制的にインクルードする参照: http://www.renaissancesoftware.net/blog/archives/136
69Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
ベンダーのコンパイラにロックインするヘッダファイル
#if defined(_ACME_X42) typedef unsigned int Uint_32; typedef unsigned short Uint_16; typedef unsigned char Uint_8; typedef int Int_32; typedef short Int_16; typedef char Int_8; #elif defined(_ACME_A12) typedef unsigned long Uint_32; typedef unsigned int Uint_16; typedef unsigned char Uint_8; typedef long Int_32; typedef int Int_16; typedef char Int_8;#else #error <acmetypes.h> is not supported for this environment#endif
70
Agile Japan - Tokyo [email protected]
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
#includeテストダブルを作成する
#include <stdint.h>
typedef uint32_t Uint_32;typedef uint16_t Uint_16;typedef uint8_t Uint_8;
typedef int32_t Int_32;typedef int16_t Int_16;typedef int8_t Int_8;
#endif /* ACMETYPES_H_ */
ベンダー固有インクルードへの依存を制限する
71Agile Japan - Tokyo 2013
Copyright © 2008-2013 James W. GrenningAll Rights Reserved. For use by training attendees.
質問があればTwitterでどうぞhttp://twitter.com/jwgrenning
私の本の翻訳はこちらhttp://www.oreilly.co.jp/books/
9784873116143/
linkedinアカウントはこちらhttp://www.linkedin.com/in/jwgrenning(メッセージに今日の研修で知り合った
ことを書いてください)
http://www.renaissancesoftware.nethttp:// www.jamesgrenning.com
質問
72