いづいづブログ

娘2人をもつ札幌のシステムエンジニア。自分で勉強したこととか日常のこととかを残しています。アジャイル札幌スタッフ。パクチー大好き。激辛大好き。

TDDの基本

f:id:izumii-19:20190606155447p:plain:w600


20019/6/15(土)に札幌では8年ぶりのTDD Boot Camp(TDDBC)が開催されます。

agilesapporo.doorkeeper.jp

8年前のTDDBCには私も参加していたはずなのですが、そのときはまだTDDについて全然理解していなかったのと、その後TDDを実践する機会がゼロだったので、ほぼすべてを忘れてしまいました。

というわけでTDDの基本から。

@t_wadaさんの[動画で解説]和田卓人の“テスト駆動開発”講座全20回分の内容を自分なりにまとめました。

この動画おすすめです。@t_wadaさん以外の観点も入っていたりコメントでリアルツッコミが入ったりしていて面白いし、入門としてはすごくいいんじゃないかなー。

gihyo.jp

第12回目までがTDDの入門的な内容になっているのでそこまでをまとめました。

テスト駆動開発(TDD)

アジャイル開発宣言に携わったうちの1人であるKent Beckが考案したプログラム開発手法の一種。 以下の3つの基本ステップを1サイクルとして、繰り返しながらコードをより良くしていく方法。

  1. これから書く機能に対するテストを1つ書き、テストが失敗することを確認する(レッド)
  2. 失敗するテストを通すための”最低限のコード”を実装する(グリーン)
  3. リファクタリングを行う(リファクタリング

「最初にテストを1つ書き、次にそのテストがグリーンになるような最低限のコードを実装する」ということは、言いかえるとテストがあってコードが生まれるということ。

すなわちテストが開発を駆動していくから、テスト駆動開発という。

1つのテストに対してこの3つのステップを回し、それが終わったら次のテストを書き…を繰り返しながらコードをより良くしていく。

ここで注意したいのは最初からいくつもテストを書かないということ。

「小さい単位で確実にそしてスピーディーに、レッド⇒グリーン⇒リファクタリングを積みかさねる」というのがとても大事。

小さく早くステップを実行することで、息を吸うようにテストコードを書き、息を吐くようにプロダクトコードを書く。「テストがあって開発が駆動する」というのを習慣にするということを目指す。

そのためにはこの1つずつテストを書くというのがとても大事。

"テスト"という言葉の定義

"テスト"という言葉には、実は以下のように目的によって複数の意味合いに分かれている場合が多い。が、そもそもこの分類を意識していない場合使い分けられていない。 ここで"テスト"という言葉の分類と、TDDにおける"テスト"とはなにかを考える。

Developer Testing

開発者が開発者(未来の自分も含む)やチームのために行うテストで、自分で書いたコードを自分でテストする。

こうすることですばやくフィードバックを得られ、その結果コード、しいては開発全体がより良いものになっていく。

単体テスト結合テストはDeveloper Testingに属する。

Customer Testing

お客様側からみたときにその機能が要件を満たしている/満たしていないを判断するためのテスト。 このテストが通らなければ、お客様側から見たときに「機能は完成していない」という判断になる。 テストが通らない = 受け入れることはできないというのが明確なので、進捗や受け入れ状態が明確になる。

受け入れテストはCustomer Testingに属する。

QA Testing

品質管理、品質保証を目的としたテスト。 上2つは機能に対するテストであるのに対し、QA Testingでは非機能の観点でのテストも含まれる。 「機能」という単位に限らず、システム全体としての品質を保証するためのテスト。

非機能テストはQA Testingに属する。

TDDで指し示す"テスト"はDeveloper Testingである。

TDDをどうやって学んでいけばいいか

@t_wadaさんのおすすめの学び方。

1.写経。おすすめ本は、 テスト駆動開発や『WEB+DB PRESS Vol.35』『WEB+DB PRESS Vol.37』*1

2.セミナーとか経験者とのペアプロやレビュー。たとえばテストを書くリズムとかサイクルとか、そういった写経ではわからないことが体感できる。

サイクルをどうまわせばよいか

1. 受け入れテストを書く

最初にやるのは「自分のための受け入れテスト」を書くこと。(=大きいテスト)

プログラムレベルのテストを考えるのではなく、「もしこの機能ができあがったとしたらこういうことができてほしい(=ToBe)」という外側の視点から書く。ブラックボックスに近い。

テスティングフレームワークを使って行う。この時点ではプログラムは存在しないので当然レッドになる。

2. 大きいテストを満たすタスクを洗い出す

大きな機能を実現するために必要な内部の設計行っていく。1. の受け入れ条件を満たすために必要となる小さな機能をタスク(箇条書き)にしていく。

3. 小さな機能に対し3ステップを実施する

先に「大きな機能の受け入れテスト」という大きい視点で考え、それを実現するための小さなタスクを考えて、そのテストを1つずつ書く。(=小さいテスト) これを繰り返す形でテスト駆動していく。

言い換えると「小さいテストをグリーンにする」を積み上げることで、最初はレッドだった大きいテストをグリーンにするということを目指す。

f:id:izumii-19:20190606125205j:plain:w400
小さいテストの総和 = 大きいテスト

テストの資産価値

動画を最後まで読んでいくとこの「テストの資産価値」について考えておくことはとても大事なことなんだと感じたので、まとめる。

「小さいテストがグリーンになることの総和=大きいテストがグリーンになる」は前述のとおりだが、ここで問題。 仕様変更などによって小さいテストが1つでもレッドになると=大きいテストがレッドになるということであり、あまりにテストの単位を小さくしてしまうとすぐ真っ赤なテストになってしまう。

そうなった場合に小さなテストを1つ1つ直していく必要があるのか?(これはけっこう現場あるあるだなぁ。)

考え方としては、まず小さなテストが赤くなった理由を考える。

  • 仕様変更によって、もうこのテストが存在する意味がないのであれば外す。
  • 仕様変更によって、テストの内容を変更する必要があるのであれば、テストをメンテナンスしてグリーンにする。

いきなり「赤くなった小さいテストをどうするか」に注目するのではなく、仕様変更後の大きいテストの受け入れテストを満たすために小さなテストはどうなっているべきかを考える。

その上で、価値のなくなった小さいテストを外し、必要な小さいテストをグリーンにすることで、仕様変更後の大きいテストをメンテナンスしていく感じ。

どうもうまく説明できない。けどすごく大事な気がする。

リファクタリングをどこまでやるか

目安としては2つある。1つは「重複がなくなるまで」。これはDRY(Don't repeat yourself)⁠に基づくもの。 次の目安は「開発者の不安がなくまるまで」。例えばコードを書いていてここがモヤっとするとか、この名前がしっくりこないなどの不安に対してリファクタリングを行う。

リファクタリングそのものはお客様にとって機能を提供するものではなく、お客様への価値は変わらないため、やめどきを決めておくことが大切である。*2

*1:Vol.35とVol.37はもう販売していないがWEB+DB PRESS総集編[Vol.1~102] にまとめられている

*2:「開発者にリファクタリングさせたらいつまでもやり続けるため永遠にプロダクトが完成しない」と言われるほど、開発者は無限にリファクタリングしたがります。