Some Days You Get the Bear

IT系エンジニアの、日々の気づきや考えたこと。

TDD は組込みを救うことができるかも?

『テスト駆動開発による組み込みプログラミング』を読んでいるところ。
まだ(15章中の)4章までしか読んでないのだけど、すんごい、いい本ですよ!


1章「テスト駆動開発には、
TDD とはなんぞやということがコンパクトにとてもわかりやすく書いてある。
この章を読むだけでも十分価値のある本だ。
私はこれで「目が覚めた」感じがした。

ある製品の不具合のことが書いてある。

(以下、青字で引用。一部は色を変えず私の言葉でまとめている。)

多くのレビュアーは正しい振る舞いを見つけられなかった。(...)
正しいことを確実に知る方法は、実際にコードを動かすことだけだ。

おぉー、やっぱり「動かさないとわからない」のだ!

私たちには TDD が必要だ。なぜなら私たちは人間であり、間違いをするからだ。

これが なぜ TDD が必要なのか、の答えだ。なんとシンプルではないか。
こういう当たり前にいまさらながら気づかされる。

TDD とは何か。簡単にまとめるとこうだ。

  1. まずテストを書く。テストは小さく、少しずつ書く。
  2. このテストが成功するように、コードを追加する。
  3. 全てのテストを実行して、全てが成功することを確認する。
  4. もちろんテストは自動化だ。間違いがあればここですぐにわかる。
  5. 間違いを直して、あとは表現を改善(リファクタリング)する。

書いてしまうと、「なんだこれだけか」という感じだが、
本文中では具体的な例を挙げながら、1ステップずつ解説をはさみながら、
そしてテストを書き進めていく中で、何度も繰り返し、目的や意味を説明してくれる。
1章だけでなく、これ以降の章も全てそうだ。
読者がおいてきぼりにならないよう、とてもていねいに、くどいくらいに何度も。
何度も説明することでようやくちゃんとわかっていくという、こちらの理解のプロセスを意識してのことだと思う。

要はテストによってここから先にバグが入り込む余地をなくすのだ。
「バグの予防」と書いてある。
バグはあとになればなるほどわかりにくくなる
(そしてこれがこれがバグ改修を計画不可能なものにし、多大なコストを生む!)。
小さく変更したあとですぐに対処する。このことで時間と労力を減らそうというのだ。

「ソフトウェア万力」という言葉も紹介されていた。

「変化を検出するテストが手元にあるということは、コードを万力で固定するようなものだ。
これによってコードの振る舞いが固定される。
コードを変更しても、一度に一部の振る舞いしか変更していないことがわかる。
要するに、自分がやっている作業がコントロールできるのだ。」

TDD をやると楽しい!、とも書いてある。動くと楽しい。そりゃそうだ。
フィードバックがあることが我々を安心させてくれるし、目の前の仮題に十分に集中させてくれる。


2章「テスト駆動ツールと約束事」では、ユニットテストハーネスの紹介がされている。
いわゆる xUnit だ。
これから出てくるコードを読んでいくため、そして自分自身で書いていくための導入となっている。

章の最後に、約束事として「4フェーズパターン」というものが紹介されている。

  • 準備 (Setup): テストの事前条件を確立する。
  • 実行 (Exercise): システムに対して何かする。
  • 検証 (Verify): 期待される結果をチェックする。
  • 後片付け (Cleanup): テストした後、テスト対象のシステムを初期状態に戻す。

テストをきれいに保つため、テストにはこのパターンを見えるようにしよう。
このパターンに従うことで、テストの読み手は何がテストされるのかすぐに判断できるようになる。

こんなふうに、必要なことが必要なタイミングで1つずつ的確に出てくるのだ。
出てくる順番も非常に気を遣って書いて下さっるのだと思う。


3章「Cモジュールにとりかかる」では、
実際にデバイスドライバ(!)のコードを対象として、これのテストを書いていくところを見せてくれる。
これも非常に具体的だ。だから本当に「TDD のテストの書き方」がわかってくる。

テストがうまく機能しているか確かめるため、テストを失敗させよう。だとか、
テストの前にコードを書いてはいけない。だとか、
戻り値をハードコートすることから始めよう。だとか、
現在のテストに必要とされる以上のコードを書きたくなる誘惑に耐えてほしい。だとか、
引用してたらキリがないぞ!

本当に具体的だ。こういうことはテストハーネスの使い方を知っただけではわからないだろう。

そしてもちろん、これは組込み向けの本だ。

LED がマップされたアドレスはおそらくリードオンリーだろう。

のような、よくある例で進んでいく。


イメージをうまく伝えてくれるという点で、「例え」の使われ方も効果的だ。

「あとでデバッグ型プログラミング (DLP:Debug-Later Programming)」では、
成虫になったバグを追いかけて、捕まえなくてはならない。
テスト駆動開発では、幼虫の段階でバグを発見できるので、成虫になってコードに群がる前にひねり潰してしまえるのだ。

DLP をやるということは、一気に川を渡ろうと、助走をつけて大きくジャンプするようなものだ。
川幅がせまければ、なんとかなるかもしれない。
だが、たいていの場合、結局は川に落ちてしまい、予想外の川の流れと戦いながら向こう岸へと渡ることになるだろう。

新しい橋ができあがる前に、古い橋(現在動いているコード)を燃やしてはいけない。


TDD の立ち位置は非常にうれしい。開発者にとっては、すごく気持ちが楽になる

私は(...)もともとは予測していなかったが、TDD を進めるうちに詳細が明らかになったテストを追加していった。
(...)これは当たり前のことで、どこも間違っていない。
開発を進めていくにつれ、私たちは多くのことを学んでいく。理解が進んだ結果として、新しいアイディアが浮かんでくるのだ。

「考慮漏れ」への対処、ではなくて、テストからのフィードバックとしての当然の対応だと言っている。

今までの我々の現場ではこうだ。

  「なぜテストするまで気が付かなかったんだ!」
  「どうしてこんな簡単な間違いを犯すんだ!」
  「どこの工程で(バグが)混入したんだ!?」

もう考え方を変えよう。
動かさないとわからないなら、とっとと動かしてみるしかないではないか。

組込みの環境で実際に動く環境をつくることは大変なことだ
(そもそもそこが第一のハードルだ)。
そして、非ターゲット環境で、どこまで意味のあるコードが書け、テストが可能なのか
疑問はまだある。
それでも、この本で述べられている思想はとても大事なことだと思う。
どうにかして組込みの環境でやってみたい。そう思わせてくれる。

さあ、先へ進んでいこう。きっとヒントは見つかるはずだ。
ワクワクしながら進んでいこう。