xUTP Chapter26. Dependency Injection

3,274 views
3,175 views

Published on

handout for xUnit Test Patterns Reading Group Japan

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,274
On SlideShare
0
From Embeds
0
Number of Embeds
23
Actions
Shares
0
Downloads
7
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

xUTP Chapter26. Dependency Injection

  1. 1. Chapter 26. Design for Testability Patterns
  2. 2. Dependency Injection
  3. 3. Dependency Injection● SUT をどう設計すれば、ランタイムに依存オブジェ クトを差し替えられるだろうか? ● クライアントが SUT に対して依存オブジェクトを提供す れば良い ● テストする際には、 SUT を依存から独立してテストでき るのが望ましいが、依存クラス名などがハードコードさ れていると難しい ● Dependency Injection は、 SUT に対する依存オブ ジェクトをテスト時に差し替えるのに使える
  4. 4. How It Works● クライアントコードやシステムの設定部(ファイル/ コード)で、 SUT が実行時に依存するオブジェクトを 渡せるようにする● この設計のポイントは、 依存が “front door” から 渡せるようになっているところ ● つまり、「依存を渡す」ということも SUT の API の一部 になる ● 依存の渡し方は、メソッドの引数、コンストラクタ、 setter などがある。
  5. 5. When to Use It● まずは、テスト時に DOC を Test Double に差し替 えるときに使う ● Static binding は依存クラス名を SUT に直接書いてし まうことで、採れる手段を大幅に狭めてしまう ● Dynamic binding は依存クラスの解決を実行時まで遅 延することで、柔軟さを得ることができる● Dependency Injection(DI) はコードをスクラッチ から設計する際には良い手段となる ● 特に TDD で設計している場合には、 DOC を Test Double で差し替えたくなるので、自然と DI が必要にな る
  6. 6. Implementation Notes(1)● 二つの解決すべき問題がある ● Test Double をどんな DOC の場合にも使えるように する必要がある。これは静的型付け言語の場合には、 Test Double をいかにも DOC のように(型を矛盾させ ること無く)渡せなければならないことを意味する ● SUT に Test Double を渡す手段を提供しなければな らない
  7. 7. Implementation Notes(2)● 型の互換性 ● (特に静的型付け言語で) DI を実現するには、 DOC と Test Double が Type Compatible でなければならな い ● 静的型付け言語では、 DOC と Test Double が共通の interface を実装していること ● 動的型付け言語では、 DOC と Test Double が同じ signature (メソッド名など) を持っていること ● 既存のコードに対して DI パターンを導入するには、まず DOC に対して「インターフェイスの抽出」を行い、その抽 出された interface に対して Test Double を書けば良 い
  8. 8. Implementation Notes(3)● Test Double の渡し方 ● さまざまなやり方があるが、名前/型のハードコードを止 め、実行時まで解決を遅らせるという基本は変わらない – Parameter Injection ● 使いたいメソッドの引数として DOC も渡す – Constructor Injection ● SUT 構築時に DOC を渡す – Setter Injection ● どこかのタイミングで setter 経由で DOC を SUT に渡す ● IoC フレームワーク (DI コンテナ) を使うと、 DI の仕組 みの実装自体も作らずに済み、つまり SUT や DOC は DI の実装に対して非依存になり、使いまわしが効くよう になる
  9. 9. Motivating Example// リファクタリング前のコードpublic void testDisplayCurrentTime_AtMidnight() { //fixture setup TimeDisplay sut = new TimeDisplay(); //exercise SUT String result = sut.getCurrentTimeAsHtmlFragment(); //verify direct output String expectedTimeString =     "<span class="tinyBoldText">Midnight</span>"; assertEquals( expectedTimeString, result);}public String getCurrentTimeAsHtmlFragment() { Calendar currentTime; try { currentTime = new DefaultTimeProvider().getTime(); } catch (Exception e) { return e.getMessage(); } // etc.}
  10. 10. Parameter Injection// テストコードpublic void testDisplayCurrentTime_AtMidnight_PI() { //Fixture setup //Test Double instantiation TimeProvider tpStub = new MidnightTimeProvider(); //Instantiate SUT TimeDisplay sut = new TimeDisplay(); //Exercise SUT using Test Double String result = sut.getCurrentTimeAsHtmlFragment(tpStub);     //Verify outcome String expectedTimeString = "<span class="tinyBoldText">Midnight</span>"; assertEquals("Midnight", expectedTimeString, result);}//プロダクトコードpublic String getCurrentTimeAsHtmlFragment(TimeProvider timeProviderArg) { Calendar currentTime; try { currentTime = timeProviderArg.getTime(); } catch (Exception e) { return e.getMessage(); } // etc.}
  11. 11. Constructor Injection// テストコードpublic void testDisplayCurrentTime_AtMidnight_CI() throws Exception { //Fixture setup //Test Double instantiation TimeProvider tpStub = new MidnightTimeProvider(); //Instantiate SUT injecting Test Double TimeDisplay sut = new TimeDisplay(tpStub); //Exercise SUT String expectedTimeString =     "<span class="tinyBoldText">12:01 AM</span>"; //Verify outcome assertEquals("12:01 AM", expectedTimeString, sut.getCurrentTimeAsHtmlFragment());}//プロダクトコードpublic class TimeDisplay { private TimeProvider timeProvider; public TimeDisplay() { //backwards compatible constructor timeProvider = new DefaultTimeProvider(); } public TimeDisplay(TimeProvider timeProvider) { //new constructor this.timeProvider = timeProvider; }...}
  12. 12. Setter Injection(1)// テストコードpublic void testDisplayCurrentTime_AtMidnight_SI() throws Exception { //Fixture setup //Test Double instantiation TimeProvider tpStub = new MidnightTimeProvider(); //Instantiate SUT TimeDisplay sut = new TimeDisplay(); //Test Double installation     sut.setTimeProvider(tpStub); //Exercise SUT String result = sut.getCurrentTimeAsHtmlFragment(); //Verify outcome String expectedTimeString = "<span class="tinyBoldText">Midnight</span>"; assertEquals("Midnight", expectedTimeString, result);}
  13. 13. Setter Injection(2)// プロダクトコードpublic class TimeDisplay { private TimeProvider timeProvider; public TimeDisplay() { timeProvider = new DefaultTimeProvider(); } public void setTimeProvider(TimeProvider provider) { this.timeProvider = provider; } public String    getCurrentTimeAsHtmlFragment() throws TimeProviderEx { Calendar currentTime; try { currentTime = getTimeProvider().getTime(); } catch (Exception e) { return e.getMessage(); } // etc. }...}
  14. 14. ご清聴 ありがとうございました

×