Slide 1

Slide 1 text

@takattata xUnit Test Patterns の序章

Slide 2

Slide 2 text

xUnit xUnitって何があるかご存知ですか?

Slide 3

Slide 3 text

JUnit(Java) SUnit(Smalltalk) NUnit(.Net) VbUnit(Visual Basic) CUnit(C) CppUnit(C++) PerlUnit(Perl) PyUnit(Python) HUnit(Haskell) HttpUnit(HTTPによる通信を擬似的に行なう ) HtmlUnit(ウェブブラウザのエミュレータ ) … xUnitでなくても、テストの体系を学べる本 xUnit

Slide 4

Slide 4 text

Goals, Principles and Smells

Slide 5

Slide 5 text

The Patterns

Slide 6

Slide 6 text

脆弱なテスト(Fragile Test)の問題 4つの点において限界があることを知るのは重要 Introduction: Fragile Test Problem

Slide 7

Slide 7 text

Behavior Sensitivity システムの動作が変更された場合、修正された機能のテストは失敗する可能性が高い Introduction: Fragile Test Problem

Slide 8

Slide 8 text

Interface Sensitivity UIを介してテスト中のシステム内のビジネスロジックをテストするのは良くない (過去よくあった?) Introduction: Fragile Test Problem

Slide 9

Slide 9 text

Data Sensitivity テストは既にシステム内にあるデータの観点から定義された test fixture(=pre-conditions, before picture)があ るが、そのデータが変更された場合、多大な努力を払わないとテストは失敗する Introduction: Fragile Test Problem

Slide 10

Slide 10 text

Context Sensitivity システム外の状態(デバイスや他アプリ, 日付等)のコンテキストの影響を受けるテストは、コンテキストを制御し ない限り、確定的に繰り返すことが困難 Introduction: Fragile Test Problem

Slide 11

Slide 11 text

Behavior Sensitivity Interface Sensitivity Data Sensitivity Context Sensitivity →これらを解決する為には、テストのフレームワークを効果的に使用する方法を学ばないといけない Introduction: Fragile Test Problem

Slide 12

Slide 12 text

回帰テスト(regression testing) プログラムに手を加えたことによる影響を確認するテスト (=デグレしてないか確認する ) 仕様としてのテスト(tests as specification) TDD この本ではTDDの書き方ではなく、テストがどのようになっているかの観点から説明 test-driven, test-first, test-last開発のどれでも役に立つので、「開発プロセスには無関心」で書いている Introduction: type

Slide 13

Slide 13 text

Pattern =繰り返し問題に対する解決策 段階がある: 高いレベルの "strategy"パターン →より詳細な "design patterns" →最も詳細な "coding idioms" へと進むためには、パターン間にリンケージが必要 Introduction: Patterns

Slide 14

Slide 14 text

Introduction: Patterns 3つのレベルで区別している - “Strategy” level patterns 大きな影響をもたらす Shared Fixture, Fresh Fixtureを使用することで異なるテストデザインパターンに繋がる - Test “Design” level patterns 特定の機能のテストを書く時, テストロジックをどう整理するかに焦点をあてる ex.) Mock Object patternとか - Test “Coding Idioms” 特定のテストをコーディングする方法, 言語固有のもの

Slide 15

Slide 15 text

Introduction: Patterns vs Principles vs Smells Principles 選択したパターンはテスト自動化の”ゴール”(テスト自動化で望まれる結果が書かれている)に影響を受ける “ゴール”はパターンを良くすることについての信念体系を体系化する、多くの”原則”によってサポートされている(詳細 については該当の章を参照のこと) Smells 使ってるパターンが持ってる、起こしがちな問題など

Slide 16

Slide 16 text

Introduction: Testing Terminology system under test(SUT) 「何をテストしているのか(whatever thing we are testing)」の略 - unit testsを書く時、テストするクラス、メソッドのこと - customer testsを書く時、アプリ全体か主要なサブシステムのこと

Slide 17

Slide 17 text

- Unit Testは特定のテストに 関してのみSUT - Unit2 Testを見た時、Unit1 SUTはUnit2 SUTが使用する ため、dependend-on component(DOC)の役割を する Testing Terminology

Slide 18

Slide 18 text

Refactoring A Test

Slide 19

Slide 19 text

Why Refactor Tests? テストは迅速な開発プロセスのボトルネックになるので、リファクタリングをした方が良い 実際に複雑なテストを簡単にする流れを見ながら、生産性の違いを感じてほしい 途中でいくつか重要なsmellsや、それを取り除くためのパターンを出していくよ

Slide 20

Slide 20 text

A Complex Test ・長い ・コメントが4行も ・ややこしい etc...

Slide 21

Slide 21 text

test conditionsを見ていこう まずここ、必ずfalseになることを期待しているなら、 Cleaning up the Verification Logic

Slide 22

Slide 22 text

失敗の呼び出しに変更しよう!直接言うべき これはExtract Method [Fowler]リファクタリング (Martin FowlerのRefactoring Improving the Design of Existing Codeからメソッド名を引 用)

Slide 23

Slide 23 text

他にもある 何故こんなにアサーションが必要なんだ?

Slide 24

Slide 24 text

予想されるオブジェクトを作って、単一のアサーションを使おう これはPreserve Whole Object[Fowler] リファクタリング

Slide 25

Slide 25 text

でも、待って! なんで if 文がテストに出てくるの? Conditional Test Logic は排除していこう 幸運にも、今回はGuard Assertionが使える

Slide 26

Slide 26 text

if … else fail() … のところを同じ条件のアサーションに変えるだけ これをもっと短くしたいなら、 Extract Methodリファクタリングを使用して Custom Assertionを定義しよう こうなる ↓

Slide 27

Slide 27 text

良き! 次は finallyブロック に注目しよう

Slide 28

Slide 28 text

そもそもこのコードには致命的な欠陥がある Cleaning up the Fixture Teardown Logic

Slide 29

Slide 29 text

もしdeleteObjectで失敗したら?のテストがな い! とはいえ、右の様なテストを管理するのはあり 得ない! この問題にはComplex Teardownがある 問題の根本を解決することはできないが、 tearDownメソッドに入れてコードを細分化しよ う

Slide 30

Slide 30 text

案1: “テスト間で分割されない ”Shared Fixtureを 使うことで、最初にオブジェクトを作らなくて 良くなる が、問題が2つ 1. 共有されたfixtureを介してやり取りす ることで、Unrepeatable Test, Interacting Tests(不安定なテスト)を 含むtest smellsが多くなる 2. 共有されたfixtureから使われるオブ ジェクトへの参照がMystery Guests になる → 今回は却下

Slide 31

Slide 31 text

案2: Fresh Fixtureを使う 自動でGCされるメモリ内のfixtureを使う が、問題が1つ 1. 作成するオブジェクトが永続的なもの (DBに保存されているなど )では機能しな い → 今回は採用 実現方法: テスト自動化フレームワークに、作成したオブ ジェクトを登録する方法を追加して、削除する

Slide 32

Slide 32 text

テストクラスに ・オブジェクトを確保しておくリスト ・追加する関数 ・削除する関数 を準備して、使う

Slide 33

Slide 33 text

良くなった が、まだいける たとえば、変数を初期化する必 要はあるだろうか? これはfinallyブロックのためだっ たから、宣言と初期化を合体さ せよう!

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Cleaning up the Fixture Setup Extract Methodリファクタリングを使用してCreation Methodを定義しよう オブジェクトを作成して、registerTestObjectを呼び出す関数をそれぞれ作ろう 利点: 読みやすくなる + カプセル化されるからコンストラクタが変更された時にメンテナンスしやすい

Slide 36

Slide 36 text

まだ問題がいくつかある 1. fixtureが予想している結果とどう関連しているか分かりづらい 2. Hard-Coded Test Dataを使っている (Unrepeatable Test, Interacting Tests, Test Run Warが起こる可能性が出る ) 解決法: 各テストに固有の値を作って、その値を元にテスト用に作成したオブジェクトで使う (=Anonymous Creation Method) ←今までの引数は関数内に値を移した Addressも → customerで 使いたいだけ createACustomer関数 内でやれば良いね? (Extract Method再び)

Slide 37

Slide 37 text

これで、テストで検証しているロジックに影響するものだけになった でも、まだ2回繰り返されているハードコーディングされたテストデータがある Replace Magic Number with Symbolic Constantリファクタリングで、 数字の意味を明確にしよう

Slide 38

Slide 38 text

ついでに、計算が分かりにくいので Introduce Explaining Variableリファクタリングで文書化してみた このコードはTests as Documentationの役割を完全に満たしている(テストの”ゴール”に関わる) The Cleaned Up Test

Slide 39

Slide 39 text

Writing More Tests Q: 毎度こんなに色々書くの ?? A: 再利用出来る Test Utility Methods があるし、 アプリのテストの為に Higher Level Language を定義している 「先ほどの内容をUtilityで書き 換えたけど、2分で書けたよ」