継承を用いたコードのテスト小林 裕2013/6/19
目次• 前回より– 役割のテストを使ったテストダブルの検証• 継承されたコードのテスト– 継承インターフェース– サブクラスの責任– 固有な動作のテスト• さいごに2013/6/19 2
役割テストを使ったテストダブルの検証2013/6/19 3
疑問class DiameterDoubledef diameter10endendclass GearTest < MiniTest::Unit::TestCasedef test_calculates_gear_inchesgear = G...
テストのモジュール化class WheelTest < MiniTest::Unit::TestCaseinclude DiameterizableInterfaceTestdef setup@wheel = Wheel.new(26, 1.5...
リファクタリング• プログラムの振る舞いを変えることなく、ソースコードを整理すること2013/6/19 6
テストダブルの検証 (1/2)2013/6/19 7class DiameterDoubledef diameter10endendclass DiameterDoubleTest < MiniTest::Unit::TestCaseinclu...
テストダブルの検証 (2/2)2013/6/19 8class DiameterDoubledef width10endendclass DiameterDoubleTest < MiniTest::Unit::TestCaseinclude ...
テストダブルの検証• 役割に対して共有可能なテストモジュール2013/6/19 9
継承されたコードのテスト2013/6/19 10
スーパークラスclass Bicycleattr_reader :size, :chain, :tire_sizedef initialize(args={})@size = args[:size]@chain = args[:chain]||...
サブクラスclass RoadBike < Bicycleattr_reader :tape_colordef post_initialize(args)@tape_color = args[:tape_color]enddef local_s...
共通インターフェース (1/2)module BycycleInterfaceTestdef test_responds_to_default_tire_sizeassert_respond_to(@object, :default_tire_...
共通インターフェース (2/2)class BicycleTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestdef setup@bike = @object = Bicycle...
サブクラスの責任• サブクラスの動作確認• スーパークラスによる強制2013/6/19 15
サブクラスの動作確認 (1/2)module BicycleSubclassTestdef test_responds_to_post_initializeassert_respond_to(@object, :post_initialize)...
サブクラスの動作確認 (2/2)class RoadBikeTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestinclude BicycleSubclassTestdef se...
スーパークラスによる強制class BicycleTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestdef setup@bike = @object = RoadBike.ne...
固有な動作のテスト• サブクラスにおける動作のテスト• スーパークラスにおける動作のテスト2013/6/19 19
サブクラスにおける動作のテストclass RoadBikeTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestinclude BicycleSubclassTestdef set...
スーパークラスにおける動作のテストclass StubbedBike < Bicycledef default_tire_size0enddef local_spares{saddle: painful}endendclass BicycleT...
さいごに• テストは不可欠• アプリケーションは高レベルで抽象化– 変更に対して安全性のテストが必要• テストは一度、適切な場所に• 依存関係の管理• インターフェースの生成• Persist, Practice, Experiment, Im...
ありがとうございました2013/6/19 23
Upcoming SlideShare
Loading in …5
×

継承を用いたコードのテスト

456 views

Published on

Published in: Education, Business, Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
456
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

継承を用いたコードのテスト

  1. 1. 継承を用いたコードのテスト小林 裕2013/6/19
  2. 2. 目次• 前回より– 役割のテストを使ったテストダブルの検証• 継承されたコードのテスト– 継承インターフェース– サブクラスの責任– 固有な動作のテスト• さいごに2013/6/19 2
  3. 3. 役割テストを使ったテストダブルの検証2013/6/19 3
  4. 4. 疑問class DiameterDoubledef diameter10endendclass GearTest < MiniTest::Unit::TestCasedef test_calculates_gear_inchesgear = Gear.new(chainring: 52,cog: 11,wheel: DiameterDouble.new)assert_in_delta(47.27,gear.gear_inches,0.01)endend2013/6/19 4Diameterizable のインターフェースは ‘width’ に変更されたが、これらテストダブルと Gear は ‘diameter’ から変更していないテストを通過しなくなるのでは?
  5. 5. テストのモジュール化class WheelTest < MiniTest::Unit::TestCaseinclude DiameterizableInterfaceTestdef setup@wheel = Wheel.new(26, 1.5)enddef test_implements_the_diameterizable_interfaceassert_respond_to(@wheel, :width)enddef test_calculates_diameter# ...endend2013/6/19 5この部分をモジュール化
  6. 6. リファクタリング• プログラムの振る舞いを変えることなく、ソースコードを整理すること2013/6/19 6
  7. 7. テストダブルの検証 (1/2)2013/6/19 7class DiameterDoubledef diameter10endendclass DiameterDoubleTest < MiniTest::Unit::TestCaseinclude DiameterizableInterfaceTestdef setup@object = DiameterDouble.newendendclass GearTest < MiniTest::Unit::TestCase# ...endDiameterDoubleTestFAIL test_implements_the_diameterizablie_interfaceExpected #<DiameterDouble:…> (DiameterDouble)to respond to #width.GearTestPASS test_calculates_gear_incheswidth に変更
  8. 8. テストダブルの検証 (2/2)2013/6/19 8class DiameterDoubledef width10endendclass DiameterDoubleTest < MiniTest::Unit::TestCaseinclude DiameterizableInterfaceTestdef setup@object = DiameterDouble.newendendclass GearTest < MiniTest::Unit::TestCase# ...endDiameterDoubleTestPASS test_implements_the_diameterizable_interfaceGearTestERROR test_calculates_gear_inchesundefined method ‘diameter’for #<DiameterDouble:0x0000010090a7f8>gear_test.rb:35:in ‘gear_inches’gear_test.rb:86:in ‘test_calculates_gear_inches’class Geardef gear_inchesratio * wheel.widthend#...end
  9. 9. テストダブルの検証• 役割に対して共有可能なテストモジュール2013/6/19 9
  10. 10. 継承されたコードのテスト2013/6/19 10
  11. 11. スーパークラスclass Bicycleattr_reader :size, :chain, :tire_sizedef initialize(args={})@size = args[:size]@chain = args[:chain]|| default_chain@tire_size = args[:tire_size]|| default_tire_sizepost_initialize(args)enddef spares{ tire_size: tire_size,chain: chain}.merge(local_spares)enddef default_tire_sizeraise NotImplementedErrorend# サブクラスでオーバーライドされる可能性があるdef post_initialize(args)nilenddef local_spares{}enddef default_chain‘10-speed’endend2013/6/19 11
  12. 12. サブクラスclass RoadBike < Bicycleattr_reader :tape_colordef post_initialize(args)@tape_color = args[:tape_color]enddef local_spares{tape_color: tape_color}enddef default_tire_size‘23’endend2013/6/19 12ヒエラルキー内のすべてのオブジェクトをリスコフの置換原則に従って生成するヒエラルキー内のすべてのオブジェクト間で共通のテストが共有可能
  13. 13. 共通インターフェース (1/2)module BycycleInterfaceTestdef test_responds_to_default_tire_sizeassert_respond_to(@object, :default_tire_size)enddef test_responds_to_default_chainassert_respond_to(@object, :default_chain)enddef test_responds_to_chainassert_respond_to(@object, :chain)enddef test_responds_to_sizeassert_respond_to(@object, :size)enddef test_responds_to_tire_sizeassert_respond_to(@object, :tire_size)enddef test_responds_to_sparesassert_respond_to(@object, :spares)endend2013/6/19 13
  14. 14. 共通インターフェース (2/2)class BicycleTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestdef setup@bike = @object = Bicycle.new({tire_size: 0})endendclass RoadBikeTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestdef setup@bike = @object = RoadBike.newendend2013/6/19 14BicycleTestPASS test_responds_to_default_chainPASS test_responds_to_tire_sizePASS test_responds_to_chainPASS test_responds_to_sparesPASS test_responds_to_default_tire_sizeRoadBikeTestPASS test_responds_to_default_chainPASS test_responds_to_tire_sizePASS test_responds_to_chainPASS test_responds_to_sparesPASS test_responds_to_default_tire_size
  15. 15. サブクラスの責任• サブクラスの動作確認• スーパークラスによる強制2013/6/19 15
  16. 16. サブクラスの動作確認 (1/2)module BicycleSubclassTestdef test_responds_to_post_initializeassert_respond_to(@object, :post_initialize)enddef test_responds_to_local_sparesassert_respond_to(@object, :local_spares)enddef test_responds_to_default_tire_sizeassert_respond_to(@object, :default_tire_size)endend継承するかは自由サブクラスによって実行2013/6/19 16
  17. 17. サブクラスの動作確認 (2/2)class RoadBikeTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestinclude BicycleSubclassTestdef setup@bike = @object = RoadBike.newendendclass MountainBikeTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestinclude BicycleSubclassTestdef setup@bike = @object = MountainBike.newendend2013/6/19 17RoadBikeTestPASS test_responds_to_default_tire_sizePASS test_responds_to_sparesPASS test_responds_to_chainPASS test_responds_to_post_initializePASS test_responds_to_local_sparesPASS test_responds_to_sizePASS test_responds_to_tire_sizePASS test_responds_to_default_chain併せてインクルードすることで正しくサブクラスを生成することが可能
  18. 18. スーパークラスによる強制class BicycleTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestdef setup@bike = @object = RoadBike.new({tire_size: 0})enddef test_forces_subclasses_to_implement_default_tire_sizeassert_raises(NotImplementedError) {@bike.default_tire_size}endend2013/6/19 18BicycleTestPASS test_responds_to_default_tire_sizePASS test_responds_to_sizePASS test_responds_to_default_chainPASS test_responds_to_tire_sizePASS test_responds_to_chainPASS test_responds_to_sparesPASS test_forces_subclasses_to_implement_default_tire_size
  19. 19. 固有な動作のテスト• サブクラスにおける動作のテスト• スーパークラスにおける動作のテスト2013/6/19 19
  20. 20. サブクラスにおける動作のテストclass RoadBikeTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestinclude BicycleSubclassTestdef setup@bike = @object = RoadBike.new({tape_color: red‘})enddef test_puts_tape_color_in_local_sparesassert_equal red, @bike.local_spares[:tape_color]endend2013/6/19 20RoadBikeTestPASS test_responds_to_default_chainPASS test_responds_to_default_tire_sizePASS test_puts_tape_color_in_local_sparesPASS test_responds_to_sparesPASS test_responds_to_sizePASS test_responds_to_local_sparesPASS test_responds_to_post_initializePASS test_responds_to_tire_sizePASS test_responds_to_chain
  21. 21. スーパークラスにおける動作のテストclass StubbedBike < Bicycledef default_tire_size0enddef local_spares{saddle: painful}endendclass BicycleTest < MiniTest::Unit::TestCaseinclude BicycleInterfaceTestdef setup@bike = @object= Bicycle.new({tire_size: 0})@stubbed_bike = StubbedBike.newenddeftest_forces_subclasses_to_implement_default_tire_sizeassert_raises(NotImplementedError) {@bike.default_tire_size}enddef test_includes_local_spares_in_sparesassert_equal @stubbed_bike.spares {tire_size: 0,chain: 10-speed,saddle: painful}endend2013/6/19 21BicycleTestPASS test_responds_to_sparesPASS test_responds_to_tire_sizePASS test_responds_to_default_chainPASS test_responds_to_default_tire_sizePASS test_forces_subclasses_to_implement_default_tire_sizePASS test_responds_to_chainPASS test_includes_local_spares_in_sparesPASS test_responds_to_size
  22. 22. さいごに• テストは不可欠• アプリケーションは高レベルで抽象化– 変更に対して安全性のテストが必要• テストは一度、適切な場所に• 依存関係の管理• インターフェースの生成• Persist, Practice, Experiment, Imagine2013/6/19 22ResponsibilitiesDependenciesInterfacesDucksInheritanceBehavior sharingCompositionTesting
  23. 23. ありがとうございました2013/6/19 23

×