リファクタリングと テストの関係 t-wada http://d.hatena.ne.jp/t-wada/ @J2EE勉強会 2005年8月20日
リファクタリングとテストの関係
twadahttp://d.hatena.ne.jp/twada/
@J2EE勉強会2005年8月20日
前回のおさらい
前回の結論(1)
● TDDはテスト技術ではない
● "Test"が指し示しているものは複数ある
– 開発促進と品質保証
– 2つの"Test"に優劣はない。「違う」だけ。
– どちらも非常に重要● テストをロールから分類する
– 開発者 / 顧客 / 品質保証担当者
前回の結論(2)
● TDDの"Test"は開発(者)のためのもの
– つまり、開発促進のテスト
● TDDの良さは設計技術でありながら品質保証技術に「かなり近い」こと
難しくなったソフトウェア開発
● ソフトウェアの複雑化● ソフトウェアの大規模化● プロジェクトの短納期化
● オブジェクト指向も(それなりに)使われだした
→ソフトウェアの品質が問題に(日経ビジネス)
→「テスト」が注目されてきた
テスト分類の混乱
● 単体、ユニット、結合、機能、システム…
● 単体テストとユニットテストは同じもの?– 単体って?
– ユニットって?
● 結合テストは何を結合する?● 品質保証? 動作確認?● だれが? いつ?
テストの目的に戻ろう
● 何のためにテストするのか● 誰のためにテストをするのか● テストで何を知りたいのか
「結局、何のためにテストを行うのか」
テストをロールによって分類する
● Developer(Programmer) Test– 開発者が行う、開発促進のためのテスト– 単体、ユニット、結合 …などなど
● Customer Test– 顧客(のロールを担うひと)が行うテスト
– 従来の「受け入れテスト」が多くを占める
● QA Test– 品質保証のためのテスト– 行う人は開発者かもしれないし、テスト担当者かもしれない
DeveloperTest
CustomerTest
QATest
「テスト」
それぞれのテストの目的
● Developer Test– 開発促進– フィードバックを伴う設計行為
● Customer Test– 進捗管理– 機能要件の検証
● QA Test– 品質保証– 非機能要件の検証
DeveloperTest
開発促進
設計行為
CustomerTest
進捗管理
機能要件
QATest
品質保証
非機能要件
「テスト」
TDD != 品質保証技術
● TDDは「こうしてみようか」をテストする
● QAテストは「こうあってはならない」をテストする
TDDは品質向上技術だが、品質保証技術ではない。そもそも「品質」の話が必要
あなたにとっての「品質」とは何ですか?
TDDは設計技法です
● 文脈
– プログラミングは設計行為である(J.Reeves)
– REDは仕様の設計
– GREENは仕様の実装
– Refactoringは内部設計の改善
参考:「ソフトウェア設計とは何か?」 http://www.biwa.ne.jp/~mmura/SoftwareDevelopment/WhatIsSoftwareDesignJ.html
TDDと品質保証の関係
● 目的は違えど、手段が品質保証技術に近い● 品質保証との親和性● 帽子を変えることで品質保証モードに入れる
前回の発表のねらい
● 議論の土台を作ること● 目的の視点からみたテストの整理
– Developer Testの概念の紹介
● TDDのTはDeveloper Testであることを理解してもらう– 品質保証のための技術ではなく、開発促進のための技術
今回のお題
今日伝えたいこと(1)
● リファクタリングの目的はコードの理解や修正が簡単になるようにすること
● リファクタリングは設計意志決定のバランスをとる
今日伝えたいこと(2)
● テストがリファクタリングを妨げるのは何かがおかしい
● リファクタリングを後押しするテストと、リファクタリングの役に立たないテストがある
● 目的に沿ってDeveloper Testを使いこなそう
第一部
リファクタリングについての整理
リファクタリングをめぐる混乱
● 三週間ほど前、リファクタリングをめぐって議論が繰り広げられました
● ひとりひとりが結ぶ「リファクタリング」の像が異なるという印象
「テスト」と同じ混乱の構図か
リファクタリングの定義(1)
リファクタリング(名詞)
外部から見たときの振る舞いを保ちつつ、理解や修正が簡単になるように、ソフトウェアの内部構造を変更させること。
リファクタリングの定義(2)
リファクタリング(動詞)
一連のリファクタリングを行って、外部から見た振る舞いの変更なしに、ソフトウェアを再構築すること。
リファクタリングの解釈のブレ
● 外部から見た振る舞い
– 外部?
– 振る舞い?
– public? publish?
● テストで担保する?● パフォーマンス?
「テスト」と同じ混乱の構図
● 違う内容のものを同じ名前で呼んでいる– 「テスト」の意味合いの違い– 「外部から」の意味合いの違い
誰が、何のために、で再整理しましょう
リファクタリングの「誰が」
● これは、私たちプログラマーです● 自分達で、自分達のコードをわかりやすく、シンプルにするのがリファクタリング– 他の人が書いたコードを理解のためにリファクタすることはあるが、特殊例
リファクタリングの「何のために」
● シンプル設計● コードを理解しやすく● コードを修正しやすく● コードをシンプルにすること ≒ 設計をシンプルにすること
大目的
「理解や修正が簡単になるように」
何がリファクタリングへと駆り立てるのか(RtPより)
● 新しいコードを追加しやすくしたいため● 既存のコードの設計を改善したいため● コードをより良く理解したいため● コーディングの不快感を減らしたいため
リファクタリングの「いつ」
● 教条的なことを言うなら、「常に」です● 気づいたときに
● サイクルに織り込んでいるのがTDD
● 「大きいリファクタリング」は、話が別● 小規模なリファクタリングは機械でも出来るし、いつもやっていることです
– rename系、extract系、inline系
リファクタリングは何ではないのか
● 非公開メソッドの中だけではない● パフォーマンスチューニングではない● バグフィクスではない● 他人のひどいコードの手直しではない
● 手戻りではない (Refactoring is not rework)
パフォーマンスチューニング?
● パフォーマンスのためにリファクタすることはほとんどない
● まず計ってからものを言うべし● 普通はデータベースがボトルネック、コードを見るよりExplain Planするべし!
「シンプル」とは何か
● コンテクストに依存する– チームで決めること– チームによってなにがシンプルかが変わる
● パターンはシンプルさのために使う
– Compositeの方が単純なとき
– Strategyの方が単純なとき● 分岐から構造へ
まとめると
リファクタリング、私の理解
● 目的は、理解や修正が簡単になるように● 設計行為としてのリファクタリング
● 外部からみた振る舞い
– publishedメソッドの事前条件と事後条件のこと
– publishedメソッドの変更もときどきリファクタリングと呼んでいます
● 「テスト」
– 大きい粒度のテスト、Mockベースのテストではなくて実オブジェクトベースのテスト
テストがある
テストがない
リファクタリング
privateprotected
public published
リファクタリング前提の設計?
● 逆を考えるとわかりやすい– 「一発で正しい設計を行え、修正や改善は許可しない」と言われたら?
● 「修正が効かない」というプレッシャーをあたえると人間は脆い
● テストとバージョン管理で安全に後戻りができるようになる– 安心して前に進むことができるようになる
技術的負債
● スケジュールの無い開発は存在しない● 負債を追ってでもリリースを優先することも● リファクタリングで負債を減らしていく
● 「ええい、やめだやめだ、スクラッチから書き直そう!」とならないために
● リファクタリングは「捨てない技術」
道はひとつではない
● 「動作する、きれいなコード」● きれいな、から攻めるか● 動作する、から攻めるか
動作する(すぐには)動かない
汚い
きれい
動作する(すぐには)動かない
汚い
きれい
動作する(すぐには)動かない
汚い
きれい
動作する(すぐには)動かない
汚い
きれい
TDDのテンポ
● Red, Green, Refactor● Red, Green, Commit, Refactor, Green, Commit
「動作する」を満たしてから
「きれいな」にとりかかる
動作する(すぐには)動かない
汚い
きれい
動作する(すぐには)動かない
汚い
きれい
Red
動作する(すぐには)動かない
汚い
きれい
Red
Green
動作する(すぐには)動かない
汚い
きれい
Green
Ref
acto
r
動作する(すぐには)動かない
汚い
きれい
Ref
acto
r
Red
動作する(すぐには)動かない
汚い
きれい
RedGreen
動作する(すぐには)動かない
汚い
きれい
Ref
acto
r
Green
動作する(すぐには)動かない
汚い
きれい
Ref
acto
r
RedGreen
リターンマッチは可能だ
● 設計の善し悪しは、最初に設計したときには決まらない
● だんだんと「より良い」設計に変えていくことが出来る
– ただし、非常に大規模なアーキテクチャ変更は別
● メジャーバージョンアップ● システムのリプレース
進化的設計(Evolutionary Design)
● フレームワークは最後に出来る● ボトムアップ● フィードバック
ここで提示しているのは極論かもしれませんが、
こんな考え方もあることを紹介したいのです。
第二部
リファクタリングをサポートするテ
ストとは何か
テストの資産価値
● リファクタリングの邪魔になるようなテストは価値が低い
● テストはリファクタリングの支えになるものであって、妨げになるものではない
● どのテストもメンテナンスコストはかかる● 半年後であっても読みやすく、リファクタリングの支えになるテストは資産価値が高い
再度Developer Testを分類する必要が出てきました
Developer Testの分類のキーポイント
● 目的● 粒度● 何を担保するのか● フィードバックまでの時間● 資産価値● メンテナンスコスト● 設計改善効果
Developer Testを目的と粒度で分類する
● Unit Test– テスト対象以外は全てMock
● Assembly Test– テスト対象のコラボレータは本物を使う
● Functional Test– コンポーネントをブラックボックスとして扱う– 永続化層も本物
DeveloperTest
CustomerTest
QATest
CustomerTest
QATest
Developer Test
Unit Test
Assembly Test
Functional Test
Developer Testの分類、もうひとつの視点
インタラクションベーステスト
● テスト対象のオブジェクトがコラボレータときちんと相互作用するかどうかを検証する
● Mockを多用する
– テストの初めでコラボレータのMockを作成
– Mockにexpectationをセット
– テストの最後でMockをverify
ステートベーステスト
● テスト対象のオブジェクトの振る舞いの事前条件、事後条件を検証する
● setUpでデータを準備
● assertEquals等で事後条件のチェック
インタラクションベースステートベース
Unit Test
AssemblyTest
FunctionalTest
リファクタリングを担保するテストとは
● ある程度大きい粒度のテストでないと、リファクタリングの前後を担保できない
● インタラクションベースのテストはリファクタリングの役にたたないことが多い
リファクタリングの誤解
● 「リファクタリング前後でテストが失敗してはならない」?
担保するテストはGreen to Greenであるべし
しかし、
全てのテストがGreen to Greenとは限らない
リファクタリングの最中には
● ステートベースのテストは失敗しない
● インタラクションベースのテストは失敗してもいい
● あくまで「想定の範囲内」であれば、ですが
なぜインタラクションベースのテストは失敗してもいいのか
● 内部設計を改善するということは、オブジェクトたちの相互作用(インタラクション)も変わる可能性が高い– 責務の再配置には、インタラクションの変更がつき従う
– インタラクションが変わるのは悪いことではなく、良いこと
インタラクションベースステートベース
Unit Test
AssemblyTest
FunctionalTest
リファクタリングの支えになりにくい領域
CustomerTest
QATest
Developer Test
Unit Test
Assembly Test
Functional Test
リファクタリングの支えになりにくい領域
私のチームがテストの粒度に使っているメタファ
● はしご● 踏台● 階段
● 調子のいいときには三段飛ばし● 心配なときには一段ずつ
トップダウンとボトムアップ
● インタラクションベースのテストだけを積み上げて正解に辿り着くのは難しい
● 受け入れテストだけを拠り所に開発を行うのは足場が少なすぎる
足りないピースは?
低コストな機能(Functional)テスト
● 大きいリファクタリングのキーポイント● 機能のブラックボックステストをいかに低コストで行うかがカギ
● 例えばJ2EEであれば
– Selenium
– HttpUnit
– Cactus
Mockでテストできないもの
● オブジェクトたちが実際に協調して動くこと
● Mockは妄想。妄想でもインタラクションの設計はできる
● 詳細なシーケンス図と役割が似ている?– 違いはコードで書かれること– 図とコードの距離
インタラクションベーステスト(Mock)のデメリット
● 価値が前方(気づき)に集中する
● メンテナンスコストが結構高い● テストがなかなか資産化しない
=> 資産価値が低い
最後にちょっとMockテストの弁護をします
Mockのメリット(1)
● フィードバックの早さ
– MockのテストはGreenにするまでの時間が短い
● 例外系のテストが行いやすい– ネットワークエラー– データベースエラー
Mockのメリット(2)
● 悪い設計に気づくまでの時間が短い
– Mockのセットアップがめんどくさい
=> インタラクションが複雑
– Mockを沢山作らなければならない
=> デメテルの法則に反している
● 責任の配置を考える機会が増える
– Tell, don't Ask
– QueryからCommandへ
テスト設計の勘どころは、次回以降をご期待ください
今日伝えたかったこと(1)
● リファクタリングの目的はコードの理解や修正が簡単になるようにすること
● リファクタリングは設計意志決定のバランスをとる
今日伝えたかったこと(2)
● テストがリファクタリングを妨げるのは何かがおかしい
● リファクタリングを後押しするテストと、リファクタリングの役に立たないテストがある
● 目的に沿ってDeveloper Testを使いこなそう
Enjoy Testing!
Special Thanks
● J2EE勉強会に参加されているみなさま● 角田さん● ひがさん● 中村さん● 稚内北星学園大学様(会場提供ありがとうございます)● かくたにさん● kdmsnrさん (今回はblikiに本当に御世話になりました)
● TDD,Refactoringを編みだしたKent Beck氏● そして、それを私に教えてくれた、masarlさん
ご静聴ありがとうございました