Ruby test double
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Ruby test double

  • 2,652 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • RRのmock引数にワイルドカードが設定できることを発見できました!
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
2,652
On Slideshare
2,650
From Embeds
2
Number of Embeds
2

Actions

Shares
Downloads
8
Comments
1
Likes
4

Embeds 2

http://s.deeeki.com 1
https://twitter.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Mock、Stub勉強会(ruby) 瀬尾直利 2011/05/20
  • 2. アウトライン MockとStubとは MockとStubの使い分け Mock Frameworkの構文比較 More about RR (Double Ruby)
  • 3. MockとStubとは Test Doubleという概念の一部。 Double とは、代役のことで、テスト用にオブジェクトを 入れ替えるときに一般的に用いられる言葉。 [1] xUnit Test Patterns by Gerard Meszaros http://xunitpatterns.com/Test%20Double.html
  • 4. Test Doubleの種類Dummy 受け渡されることはあるが実際に使用されることはない。パラメータリ ストを埋めたいだけといった場合に利用されることが多い。Fake 実際に動作するように実装されてはいるが、手抜きされており製品版 には向かない。Stub テスト時の呼び出しに対してあらかじめ決められた値を返すもの。Spy 呼び出しに基づく情報を記録するスタブ。例えば、何通メールが送ら れたかだけカウントするメールサービスなどが該当する。Mock テスト時の呼び出しの仕様を表したもの。期待されない呼び出しが行 なわれた場合は例外をスローできる。期待された呼び出しがすべて行わ れたか確認できる。
  • 5. Mock サンプルコード on Mocha (1)# expect a class methodDuck.expects(:quack)Duck.quack# => verify success# expect an instance method of a real objectduck = Duck.newduck.expects(:quack)# => verify success# traditional mockingduck = mock(’duck’)duck.expects(:quack)duck.quack# => verify success
  • 6. Mock サンプルコード on Mocha (2)# expect multiple invocationsduck = mock(’duck’)duck.expects(:quack).times(3)duck.quackduck.quackduck.quack# => verify success# specifying parameters and specifying a return valueduck = mock(duck)duck.expects(:waddle).with(2).returns(3)duck.waddle(2) # 3# => verify successduck.waddle(1) # 3# => verify failure
  • 7. Stub サンプルコード on Mocha# return values and exceptionsduck = Duck.new # = mock(duck)duck.stubs(:flag).returns(true,false).then.raises(BrokenWingError)duck.flap # => trueduck.flap # => falseduck.flap # => raises BrokenWingError※ ここでは返り値のテストはしていない。
  • 8. イメージ付きました?
  • 9. アウトライン MockとStubとは MockとStubの使い分け Mock Frameworkの構文比較 More about RR (Double Ruby)
  • 10. MockとStubの使い分け Mock は呼び出しの verification をする。 Stub はそれをしない。 → で、結局どう使い分ける?テストの観点の違い[3] 相互作用(振る舞い)中心のテスト → Mock。 状態中心のテスト → Stub。
  • 11. 相互作用中心のテストテスト対象のシステムと外部のコンポーネントとの間で正しいやり取りがされるかのテスト。いわばプロトコルのテスト。外部のコンポーネントを Mock で置き換え、システムが正しい呼び出しをしているかを監視する。例えば MVC でいう Controller の単体テスト。Modelが正しく実装された時にControllerが正しく動作することが、Modelがなくても保証される。
  • 12. 状態中心のテストテスト対象のシステムが正しい結果を返すかというテスト。最終的に返ってくる結果だけが重要。外部のシステムとの統合が面倒な時に Stub を利用してテストを簡単にする。入出力の例を Stub を使って記述する。
  • 13. サンプルコード) 振る舞い中心のテストleft_tire = mock(left_tire)right_tire = mock(right_tire)robot = Robot.new(left_tire, right_tire)left_tire.expects(:move).with(-5)right_tire.expects(:move).with(+5)robot.turn_left# behaviour verificationleft_tire.verifyright_tire.verify
  • 14. サンプルコード) 状態中心のテストleft_tire = mock(left_tire).stub(:move).returns(-5);right_tire = mock(right_tire).stub(:move).returns(+5);robot = Robot.new(left_tire, right_tire)initial_position = robot.positionInitial_direction = robot.directionrobot.turn_left# state verificationassert_equal initial_position, robot.positionassert robot.direction > initial_direction
  • 15. ここまでおk?
  • 16. アウトライン Test Double MockとStubの違い Mock Frameworkの構文比較 More about RR
  • 17. Mock Frameworkの構文比較 RR Mocha Rspec/mocks Flexmockの構文を比較してみた[4]。
  • 18. Mock 期待: Userクラスのfindメソッドに引数99が渡され、 変数userが返される。RRmock(User).find(99) { user }MochaUser.expects(:find).with(99).returns(user)Spec/mocksUser.should_receive(:find).with(99).and_return(user)Flexmockflexstub(User).should_receive(:find).with(99).and_return(user).once
  • 19. Stub (1) 期待: Userクラスのfindメソッドに引数‘99’が渡された場合、 user1が、それ以外の場合 user2 が返される。RRstub(User).find(99) { user1 }stub(User).find { user2 }MochaUser.stubs(:find).with(anything).returns(user2)User.stubs(:find).with(99).returns(user1)
  • 20. Stub (2) 期待: Userクラスのfindメソッドに引数‘99’が渡された場合、 user1が、それ以外の場合 user2 が返される。Spec/mocksusers = { 99 => user1, default => user2}User.stub!(:find).and_return do |id| users[id] || users[default]endFlexmockusers = { 99 => user1, default => user2}flexstub(User).should_receive(:find).and_return do |id| users[id] || users[default]end
  • 21. 所感可読性はMochaやrspec実際のコードに近いのはRR短くかけるのはRR どう?
  • 22. アウトライン MockとStubとは MockとStubの使い分け Mock Frameworkの構文比較 More about RR (Double Ruby)
  • 23. RRの使い方[6]test/unit class Test::Unit::TestCase include RR::Adapters::TestUnit endrspec Spec::Runner.configure do |config| config.mock_with :rr # もしくは、バージョンの非互換性で動作しない場合は # config.mock_with RR::Adapters::Rspec end単独使用 require rr extend RR::Adapters::RRMethods object = Object.new mock(object).method_name {:return_value} object.method_name # :return_value が返ります RR.verify # ダブルの期待を満たしているかを検証します
  • 24. RRのメソッド mock もしくは mock! stub もしくは stub! dont_allow もしくは dont_allow! proxy もしくは proxy! instance_of もしくは instance_of!! が付いているメソッドは純粋なダブルオブジェクトを作成します。obj = MyObject.newmock(obj).hello # 上書き。Partial Mockingobj = mock! # Pure mock object. Traditional Mockingobj.hello
  • 25. Mock サンプルコード on RR どういう意味でしょうか? 意味でしょうかQ. どういう意味でしょうか?view = View.newmock(view).render(:partial => "user_info") {"Information"}次の方法でモックへ渡される引数をいくつでも許せます。 方法でモックへ渡される引数をいくつでも許せます。 でモックへ 引数をいくつでもmock(view).render.with_any_args.twice do |*args| if args.first == {:partial => "user_info} "User Info" else "Stuff in the view #{args.inspect}" endend
  • 26. Stub サンプルコード on RR どういう意味でしょうか? 意味でしょうかQ. どういう意味でしょうか?jane = User.new(Jane)bob = User.new(Bob)stub(User).find(42) {jane}stub(User).find(99) {bob}stub(User).find do |id| raise "Unexpected id #{id.inspect} passed to me"end
  • 27. dont_allow サンプルコード決して呼ばれない期待。呼び出されると例外が発生。 して呼ばれない期待。 期待 されると例外が発生。 例外dont_allow(User).find(42)User.find(42) # TimesCalledError 例外が送出されます
  • 28. mock.proxy サンプルコード返り値を置き換えない mockclass Test def method_a “a” endendtest = Test.newmock.proxy(test).method_a # expects once calledputs test.method_a # a# normal mockmock(test).method_a # expects once calledputs test.method_a # 空実際の処理をさせ、実際の処理をさせ、返り値を受け取り、偽装して返す。 をさせ 偽装して返 してview = controller.templatemock.proxy(view).render(:partial => "user_info") do |html| html.should include("John Doe") "Different html"end
  • 29. stub.proxy サンプルコード返り値をインターセプトview = controller.templatestub.proxy(view).render(:partial => "user_info") do |html| html.should include("Joe Smith") htmlend
  • 30. instance_of サンプルコードあるクラスの全てのインスタンスをStubする。あるクラスの全てのインスタンスを する。 するmock.instance_of(User).valid? { false }※ Mocha だと any_instance_of なのでもう少しわかりやすい・・・
  • 31. Spy サンプルコードメソッド呼メソッド呼び出しの記録 しの記録rspec subject = Object.new stub(subject).foo subject.foo(1) subject.should have_received.foo(1) subject.should have_received.bar # this fails
  • 32. Double graphs例) 期待 #foo メソッドが呼び出され、その返り値に対して、 期待: メソッドが呼 され、その返 して、#bar メソッドが呼び出される。 メソッドが呼 される。object = Object.newstub(object).foo.stub!.bar{:baz}object.foo.bar # success and returns :baz
  • 33. ワイルドカードanything mock(object).foobar(1, anything) object.foobar(1, :my_symbol)is_a mock(object).foobar(is_a(Time)) object.foobar(Time.now)numeric mock(object).foobar(numeric) object.foobar(99)boolean mock(object).foobar(boolean) object.foobar(false)duck_type mock(object)foobar(duck_type(:walk, :talk)) arg = Object.new def arg.walk; ‘walk’; end def arg.talk; ‘talk’; end object.foobar(arg) # expects arg has methods, walk and talk.
  • 34. ワイルドカードRanges mock(object).foobar(1..10) object.foobar(5)Regexps mock(object).foobar(/on/) object.foobar(“ruby on rails”)hash_including mock(object).foobar(hash_including(:blue => "#0000FF“, :red => "#FF0000")) object.foobar({:red => "#FF0000", :blue => "#0000FF", :green => "#00FF00"})satify mock(object).foobar(satisfy {|arg| arg.length == 2}) object.foobar("xy")any_times mock(object).method_name(anything).times(any_times) {return_value}
  • 35. 最後に 実際のコード を見てみよう!
  • 36. 参考文献[1] Test Double at XUnitPatterns.comhttp://xunitpatterns.com/Test%20Double.html[2] テストダブルhttp://capsctrl.que.jp/kdmsnr/wiki/bliki/?TestDouble[3] ricollab Web Tech Blog » Blog Archive » Mock と Stub についてhttp://blogs.ricollab.jp/webtech/2009/09/mock_and_stub/[4] brian - Introducing RRhttp://pivotallabs.com/users/brian/blog/articles/352-introducing-rr[5] An Introduction to Mock Objects in Ruby by James Meadhttp://jamesmead.org/talks/2007-07-09-introduction-to-mock-objects-in-ruby-at-lrug/[6] RR README.dochttps://gist.github.com/330284/fb3fdbf2dd91a800ed83de37b8d15ea84c3dd2c8