いきさつ
CDO「時間がないを言い訳にTDDが浸透しない」
俺「テスト・ファーストでないと実装できない体にしちゃえば、時間がなくてもやりますって」
CDO「それ、社内LTでしゃべって」
前世紀末「XP入門」のままのTDD原人のLT資料です。
※俺の環境:Java/JUnit/Eclipse/Gitlab
ネタ本
ケント・ベック
「XP エクストリーム・プログラミング入門
ソフトウェア開発の究極の手法」
和訳初版 2000/12 古い!
Test First
実装コードを書いて、そのテストをする
その順番をひっくり返す
1 今書ける一番簡単なTestCaseを一つ書く
- Dao の CRUD なら Create から
- 最初は実装もinterfaceもないから、compileもできない
- 当然のことなので敗北感はない
2 IDE の quick fix で空classや空method作る
- compile できた
- それだけで なんか達成感
- IDEに感謝。プログラマ黄金時代©Matz
3 Run Test - Red!
compile できたらすぐ Run Test.
- 実装の中身なし Test Fail
- 当然のことなので敗北感はない
4 クソコード実装
最速最短でテストを通すためのクソコード実装
許す!
- 即値のハードコーディング
- コピペ
- etc
5 Run Test - Green!
- 最速最短でテストAll Green
- なんて全能感
6 local git に commit
- TestCase 1つ
- それを満足する最速最短実装コード
- そして 1に戻る
テスト・ファーストまとめ
- 一番簡単なTestCaseをひとつ
- 空class, 空method
- Run Test - Red!
- クソコード実装
- Run Test - Green!
- local git に commit して 1へ戻る
ドーパミンサイクル
メンタルに良い。
- Redになっても敗北感なし
- Greenになるたび達成感・全能感
- 回転するたび脳内麻薬どんどん出ますよ
「条件付け」
- 実装が空で Run Testは無駄?
- Red->Green でドーパミンを出す「条件付け」
- 一度Redにしておく
- Testをコピペしたまま手を入れ忘れて、いきなり Green というのもたまによくある話
JUnit画面でドーパミン 変態?
「条件付け」で自分の脳を改造する
- Green画面 → 人生で最高の記憶を開く
- 繰り返すうちに記憶操作を意識しなくてもテンション上がる
- Plugin もあるかも
- Green画面 → お気に入り画像がpop-upする
- なければ作りなよ
だんだん簡単じゃなくなる?
- 前回のドーパミンを燃料にブン回してさらに大きな達成感・全能感を
- ドーパミン・サイクル 良循環
- TestClassのコードも組み合わせ可能なストックが蓄積していく
TestCaseレビュー
- 推奨:サイクルをブン回して、一通り(ある程度)揃ったら、Testレビュー
- WIP(Work In Progress)の Merge Request
- 仕様理解が正しいか確認してから次のステップに
- 実装のソースコードレビューはまだ先
- 今はクソコードの山
リファクタリング
Testレビュー後、クソコードの山の実装を洗練
- レビュー済みTestCase/TestCodeはFix。
- 機能の等価性を保ったまま改修=リファクタリング
- デグレ・リスク回避
1 一番簡単なRefactoring
一番簡単に「よりましな」実装コードにできることを1つだけ修正する
IDE の refactoring 機能で機械的に修正するのも1つ
よりましなコード
- コーデイング規約
- 最速最短コードとコーディング規約準拠は両立が◎
- DRY原則(Don't repeat yourself)
- リーダブルコード
- アンチパターンにはまってないか
2 Run Test
- Green?
- 一番簡単なところだったのでたぶんGreen。
- それだけでなんか達成感
- local git に commit して 1 に戻る
3 Test Red - Debug
- Red?
- なんで?敗北感
- Debug
- 1カ所だけの変更。簡単に原因はわかる
- Greenに回復したら全能感
- 手をつけちゃいけないやつだったら、元に戻して1へ
リファクタリングまとめ
- 正しいTestCaseが一揃い
- 作業の方向性の正しさを確信
- 1サイクル回すたびにコードが綺麗になる
- 脳内麻薬どんどん出ますよ。
- 予定工数を消化したら打ち切り
- remote に push してCIへ
CI 継続的インテグレーション
- 開発branchにpush
- Jenkins で全体統合テスト
- All Green なら本流へのmerge request
- ソースコードレビュー
Don't say "lazy"
テスト・ファーストは、遅くないよね?
- 初期は最速最短のクソコード
- 予定工数内で可能な限りのリファクタリング
- リファクタリング時間=0でも、要件を満たす実装は手中
- CI通してればクソコードの山のままリリースする判断も可
中間まとめ
「テスト・ファーストでないと実装できない体」
=脳内麻薬どんどん出してテスト中毒になったプログラマ
「軽い気持ちでいっぺん試してみなよ。みんなもやってるし。」
TDDの補足
元ネタがXP、エクストリームなので、いうことがいちいち極端です。
- 今書ける(主観で)一番簡単なTestCase
- (主観で) 最速最短でテストを通す
- (主観で) 一番簡単に「よりましな」実装コードにできる
何から手をつけるか、どこまでリファクタリングするか、実際には高度な判断をしてください。
似たようなレベルだから手当たり次第にやることもあります。判断コストも含めての「一番簡単」
2013-01 某デスマのロードマップ
- 2012-11-20 サービスイン
- 2013-01-xx 初回請求
- 2013-02-01 全額返金以外の通常退会、コンビニ払い
- 2013-02-21 新規Web機能リリース
- 「新機能は、お客様のご利用データが十分蓄積されてからのご提供となります。」
- 2013-04-01 サービス仕様大改訂と同時に年度切替
ブリーフィング
2013年1月から参加
- サーバーのbacklogを一掃して
- 契約系にうつる前任者の担当分
- backlog はどこで管理している?
- Redmine は個人レベル、プロジェクト運用なし
- 口頭ベース
- サービス外部仕様書 2012-07-02 こんなに古くて大丈夫か
- 仕様書はもうついていってない。現物みながら
はじめの一歩
Checkout & Build
r14105 (12/28 09:59)
JUnit Runs:602
14:19 JUnit開始
15:22 終了 Errors:64 Failures:8
TDD原人は、project取得したらすぐに Run JUnit。
なんじゃこりゃ
- 1h以上かかるだと!
- 10%も Error
- ひえー
状況把握
- 実装javaに較べて
*Test.java
が古すぎ - 最初は UnitTestを書いていたがメンテやめた?
- AllTests.java の TestSuite発見
-
Runs: 171/171 Erros:15 Failures:0
所要時間20分 - Error率は似たようなものだが、すべてが
mail smtp
- dummy の smtpサーバを教えてもらってAll Green
- 生きているTestClassはこのSuite/SubSuiteで管理中
前任者の負担を小さく、プロジェクト状況をJUnitに聞く
CI欲しい
- Run JUnit に20分以上 ローカルで毎回全部はリズムが悪い。
- 特定の branch名を取得してテストを回し続ける環境が欲しい。
- 所要時間の大半は、外部顧客管理サービスのレスポンス待ち。
- それなら、マシン性能は低くても、所要時間に影響しない。
- 仮想環境に、PostgreSQLと、smtp4dev入れて、Jenkins用branch にコミットしたら、自動テスト起動にしよう
CI環境構築
- 2013-01-07 15:00 構築開始
- 2013-01-09 10:41 JenkinsTest Green
新参者がやらかしてもこれで検出される。安心感。
DBリバースエンジニアリング
実装担当の負担を小さく、DB状況をSchemaSpyに聞く。
- スキーマ定義から自動でhtml形式のER図
- http://schemaspy.sourceforge.net/
JUnit Debugで実装把握
生きているTestCaseをDebug実行して、内部実装を把握
- 少しでもUnitTestがあったので、橋頭堡にして信頼できるところを広げていけた
当時の作業メモ
- なんと BaseDao.java を使っていない。
- 上記テストを実行しても BaseDao.java の factory では停止しない。
- BaseDao.javaは 古いUnitTestのごく一部でしか使っていないということらしい。
Redmineプロジェクト管理
- サーバータスクのRedmineチケット運用 2013/01/22~
- 2013-02-01分 からRedmineロードマップで Todo管理
四半期評価 実績
- 2/21 に新Web機能を追加対応込みで予定通りリリース。
- 3/29 のサービス仕様大改訂を予定通りリリース。
- 改訂内容は、当初企画案のMax以上まで実装
- データマイグレーションで改訂前の蓄積データを扱うときも、ロジックの2本立てを回避
四半期評価 直属上司
- 顧客側決裁権限者に商品をアピールできる機能にできた。
- ○○自動選択の実現など当初の計画にはなかった機能も実現し、施策の効果を最大化した。
- 平行してテストケースの作成も推進して頂き、今後の保守・メンテナンスコストの削減に貢献した。
追加要件も全部呑んでミッション・コンプリートです。
某プロダクト2016状況
- 2013:
Runs: 171/171 Erros:15 Failures:0
所要時間20分 - 現在: 3,009個のテスト 所要時間47分
- TestSuite管理はやめた。原則全部のTest.classがCIで走る
- JavaScript も jasmine でTest. いくつあるんだ?
TDDの敵:状況依存
- 特定の日時でないとテストできないケース
- 現在時刻が関わるテストから、テスト容易性設計を学ぶ by t_wada
- 特定のDB状況を前提にしないとテストできない
- 例:あるTABLEの1年間の履歴から、次のアクションを決める
- 対策1:setUp() の TestDataRegister()を頑張る
- 対策2:フラグを立てるロジックとフラグを見て動くロジックに分離
- 対策0:テストしにくい要件を、企画段階でDropして KISS(Keep It Simple, Sxxxxx)原則を貫く
- Multi-Thread Timing
TDDの敵:遅い実装
- ドーパミンサイクルの回転数が落ちる
- 某プロダクトだと、外部連携顧客管理サービスのレスポンス待ち
- 2013/07/24 Mock化
- Springの動的Binding
- 未解決
TDDの敵:必要のないテスト
- ドーパミンサイクルの回転数が落ちる
- 例: 該当者がいない古いタイプの契約のテストケース
- 例: 境界条件でもないのに、無闇にパラメータ値のバリエーションでTestCase増やしている
TDDの敵:Test Red で帰る人
「明日朝自分が死ぬかもしれないと思ってpushしなさい」
TDDの敵:ラスボス
- 実装を軽くみる風潮
- 「機能設計だけして、実装は委託して。君は単価高いんだから」
- 不倶戴天
最後に
- CDOと各技術大臣はTDDの味方です。
- ネタ本もトーカも古いので、最新情報はTDDBootCampとか外部リソースで追いかけて