Upcoming SlideShare
×

# Mock vs Stubs CleRB Presentation

2,547 views
2,425 views

Published on

"Mocks vs Stubs" Cleveland Ruby Brigade (CleRB) Presentation

Published in: Technology
4 Likes
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

Views
Total views
2,547
On SlideShare
0
From Embeds
0
Number of Embeds
858
Actions
Shares
0
36
0
Likes
4
Embeds 0
No embeds

No notes for slide
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• \n
• ### Mock vs Stubs CleRB Presentation

1. 1. MOCKS VS STUBS - How will we test this?
3. 3. WHO...??
4. 4. MOCKS AREN’T STUBS
5. 5. THE WAREHOUSE EXAMPLE
6. 6. CALL SEQUENCES
7. 7. describe Order do before(:each) do @warehouse = Warehouse.new @warehouse.add(:Cleveland, 50) @warehouse.add(:Akron, 25) end it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) order.fill(@warehouse) order.should be_filled @warehouse.quantity_at(:Cleveland).should == 0 end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) order.fill(@warehouse) order.should_not be_filled @warehouse.quantity_at(:Cleveland).should == 50 endend
8. 8. USING A MOCKdescribe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) order.fill(warehouse) order.should_not be_filled endend
9. 9. USING A MOCKdescribe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) order.fill(warehouse) order.should_not be_filled endend
10. 10. USING A STUB - VERBOSEdescribe Order do it "fills the order with enough items in warehouse" do @warehouse.stub!(:quantity_at).and_return(50) @warehouse.stub!(:set_quantity_at).and_return(true) order = Order.new(:Cleveland, 50) order.fill(@warehouse) order.should be_filled end it "does not fill the order with not enough items" do @warehouse.stub!(:quantity_at).and_return(50) @warehouse.stub!(:set_quantity_at).and_return(true) order = Order.new(:Cleveland, 51) order.fill(@warehouse) order.should_not be_filled endend
11. 11. describe Order do before(:each) do @warehouse = stub(warehouse_stub) setup_stub end it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) order.fill(@warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) order.fill(@warehouse) order.should_not be_filled endenddef setup_stub @warehouse.stub!(:quantity_at).and_return(50) @warehouse.stub!(:set_quantity_at).and_return(true)end
12. 12. HOW COOL IS THAT?describe Order do before(:each) do @warehouse = stub(warehouse_stub, {:quantity_at => 50, :set_quantity_at => true}) end it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) order.fill(@warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) order.fill(@warehouse) order.should_not be_filled endend
13. 13. CAN WE DO THE SAME?describe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should_not be_filled endend
14. 14. OH, SNAP!describe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should_not be_filled endend
15. 15. BUT THIS WORKS!describe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_not_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should_not be_filled endend
16. 16. BUT THIS WORKS!describe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should be_filled end it "does not fill the order with not enough items" do order = Order.new(:Cleveland, 51) warehouse = mock(:warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_not_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) order.should_not be_filled endend
17. 17. SO WHAT’S THE DIFFERENCE?
18. 18. NO, SERIOUSLY!• Stubs - Provide canned responses to messages• Mocks - Specify AND verify that certain messages should be received
20. 20. MOCKS CAN DO MOREdescribe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse)warehouse.should_receive(:quantity_at).ordered.with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).ordered.with(:Cleveland, 0) order.fill(warehouse) order.should be_filled endend
21. 21. MOCKS CAN DO MOREdescribe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse)warehouse.should_receive(:quantity_at).ordered.with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).ordered.with(:Cleveland, 0) order.fill(warehouse) order.should be_filled endend
22. 22. CHANGE THE ORDERdescribe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:set_quantity_at).ordered.with(:Cleveland, 0) warehouse.should_receive(:quantity_at).ordered .with(:Cleveland).and_return(50) order.fill(warehouse) order.should be_filled endend
23. 23. OH, SNAP!describe Order do it "fills the order with enough items in warehouse" do order = Order.new(:Cleveland, 50) warehouse = mock(:warehouse) warehouse.should_receive(:set_quantity_at).ordered.with(:Cleveland, 0) warehouse.should_receive(:quantity_at).ordered .with(:Cleveland).and_return(50) order.fill(warehouse) order.should be_filled endend
24. 24. I USED RSPEC MOCKING... ... but there are other libraries:• Flexmock• Mocha• rr
25. 25. FLEXMOCK - TEST/UNITclass TestOrder < Test::Unit::TestCase def test_fills_order_with_enough_items_at_location order = Order.new(:Cleveland, 50) warehouse = flexmock(warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) assert order.filled? end def test_does_not_fill_order_with_not_enough_items_at_location order = Order.new(:Cleveland, 51) warehouse = flexmock(warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) order.fill(warehouse) assert order.filled? == false endend
26. 26. FLEXMOCK - TEST/UNITclass TestOrder < Test::Unit::TestCase def test_fills_order_with_enough_items_at_location order = Order.new(:Cleveland, 50) warehouse = flexmock(warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) warehouse.should_receive(:set_quantity_at).with(:Cleveland, 0) order.fill(warehouse) assert order.filled? end def test_does_not_fill_order_with_not_enough_items_at_location order = Order.new(:Cleveland, 51) warehouse = flexmock(warehouse) warehouse.should_receive(:quantity_at).with(:Cleveland).and_return(50) order.fill(warehouse) assert order.filled? == false endend
27. 27. WHEN TO USE MOCKS/STUBS?• Using an external service• Verifying a protocol• Objects are complicated to create
28. 28. WHEN TO USE MOCKS/STUBS?• Using an external service• Verifying a protocol• Objects are complicated to create
29. 29. BUT HOWCAN I USE THIS?
30. 30. MEET TWEETSTATS
32. 32. LOGGED ON
33. 33. SEND TWEET
34. 34. VIEW FRIENDS
35. 35. VIEW FOLLOWERS
36. 36. ENTER THE SERVICEModel ControllerView OauthService
37. 37. THE USERS CONTROLLERclass UsersController < ApplicationController before_filter :oauth_login_required, :except => [ :callback, :signout, :index ] def new end...private def oauth_login_required logged_in? || login_by_oauth end... def login_by_oauth redirect_to self.oauth_service.login_by_oauth rescue Exception => e # The user might have rejected this application flash[:error] = "Twitter API failure (account login)" redirect_to root_url endend
38. 38. STUB IT OUT!describe UsersController do TEST_URL = twitter_test describe "GET new" do it "redirects to twitter - stub example" do # see the extra message stub, I dont need “logged_in?” oauth_service_stub = stub(:login_by_oauth => TEST_URL, :logged_in? => false) controller .stub!(:oauth_service) .and_return(oauth_service_stub) get new response.should redirect_to TEST_URL end endend
39. 39. STUB IT OUT!describe UsersController do TEST_URL = twitter_test describe "GET new" do it "redirects to twitter - stub example" do # see the extra message stub, I dont need “logged_in?” oauth_service_stub = stub(:login_by_oauth => TEST_URL, :logged_in? => false) controller .stub!(:oauth_service) .and_return(oauth_service_stub) get new response.should redirect_to TWITTER_TEST_URL end endend
40. 40. OR MOCK ITdescribe UsersController do TEST_URL = twitter_test.com describe "GET new" do it "redirects to twitter - stub example" do ... end it "redirects to twitter - mock example" do oauth_service_mock = mock(:oauth_service) oauth_service_mock .should_receive(:login_by_oauth) .and_return(TEST_URL) controller .stub!(:oauth_service).and_return(oauth_service_mock) get new response.should redirect_to TEST_URL end endend
41. 41. OR MOCK ITdescribe UsersController do TEST_URL = twitter_test.com describe "GET new" do it "redirects to twitter - stub example" do ... end it "redirects to twitter - mock example" do oauth_service_mock = mock(:oauth_service) oauth_service_mock .should_receive(:login_by_oauth) .and_return(TEST_URL) controller .stub!(:oauth_service).and_return(oauth_service_mock) get new response.should redirect_to TEST_URL end endend
42. 42. BUT WHAT’S THE DIFFERENCE? Unused Message Stuboauth_service_stub = stub(:login_by_oauth => TEST_URL, :logged_in? => false) oauth_service_mock = mock(:oauth_service) oauth_service_mock .should_receive(:login_by_oauth) .and_return(TEST_URL)
43. 43. THIS WILL FAILit "redirects to twitter - mock example" do oauth_service_mock = mock(:oauth_service) oauth_service_mock .should_receive(:login_by_oauth) .and_return(TEST_URL) oauth_service_mock .should_receive(:logged_in) .and_return(false) controller .stub!(:oauth_service).and_return(oauth_service_mock) get new response.should redirect_to TEST_URLend
44. 44. OH, SNAP!it "redirects to twitter - mock example" do oauth_service_mock = mock(:oauth_service) oauth_service_mock .should_receive(:login_by_oauth) .and_return(TEST_URL) oauth_service_mock .should_receive(:logged_in) .and_return(false) controller .stub!(:oauth_service).and_return(oauth_service_mock) get new response.should redirect_to TEST_URLend
45. 45. OK, I AM AUTHENTICATEDdef callback user_info = self.oauth_service.get_user_info(params[:oauth_verifier]) @user = User.save_user!({ :twitter_id => user_info[id], :screen_name => user_info[screen_name], :token => self.oauth_service.access_token, :secret => self.oauth_service.access_secret, :profile_image_url => user_info[profile_image_url] }) self.current_user = @user redirect_to @user # Redirect to the show pagerescue Exception => e # The user might have rejected this application. flash[:error] = "Twitter API failure (account login)" redirect_to root_urlend
46. 46. A NEGATIVE TESTdescribe "Get callback" do it "redirects to the home page when error occurs in service" do # I can use a mock oauth_service = mock(:oauth_service) oauth_service.should_receive(:get_user_info) .once .with(xyz) .and_raise(StandardError) controller.stub!(:oauth_service).and_return(oauth_service) # Exercise get callback, {:oauth_verifier => xyz} flash[:error].should == Twitter API failure (account login) response.should redirect_to root_url endend
47. 47. A NEGATIVE TESTdescribe "Get callback" do it "redirects to the home page when error occurs in service" do # I can use a mock oauth_service = mock(:oauth_service) oauth_service.should_receive(:get_user_info) .once .with(xyz) .and_raise(StandardError) controller.stub!(:oauth_service).and_return(oauth_service) # Exercise get callback, {:oauth_verifier => xyz} flash[:error].should == Twitter API failure (account login) response.should redirect_to root_url endend
48. 48. OR EXPECT IT TWICEdescribe "Get callback" do it "redirects to the home page when error occurs in service" do # I can use a mock oauth_service = mock(:oauth_service) oauth_service.should_receive(:get_user_info) .twice .with(xyz) .and_raise(StandardError) controller.stub!(:oauth_service).and_return(oauth_service) # Exercise get callback, {:oauth_verifier => xyz} flash[:error].should == Twitter API failure (account login) response.should redirect_to root_url endend
49. 49. OH, SNAP!describe "Get callback" do it "redirects to the home page when error occurs in service" do # I can use a mock oauth_service = mock(:oauth_service) oauth_service.should_receive(:get_user_info) .twice .with(xyz) .and_raise(StandardError) controller.stub!(:oauth_service).and_return(oauth_service) # Exercise get callback, {:oauth_verifier => xyz} flash[:error].should == Twitter API failure (account login) response.should redirect_to root_url endend
50. 50. A STUB WILL BE FINEdescribe "Get callback" do it "redirects to the home page when error occurs in service" do # I could use a mock # oauth_service = mock(:oauth_service) # oauth_service.should_receive(:get_user_info) .once.with(xyz) .and_raise(StandardError) # but a stub will exercise the code just fine oauth_service = stub(:oauth_service) oauth_service.stub!(:get_user_info).and_raise(StandardError) controller.stub!(:oauth_service).and_return(oauth_service) # Exercise get callback, {:oauth_verifier => xyz} flash[:error].should == Twitter API failure (account login) response.should redirect_to root_url endend
51. 51. A STUB WILL BE FINEdescribe "Get callback" do it "redirects to the home page when error occurs in service" do # I could use a mock # oauth_service = mock(:oauth_service) # oauth_service.should_receive(:get_user_info) .once.with(xyz) .and_raise(StandardError) # but a stub will exercise the code just fine oauth_service = stub(:oauth_service) oauth_service.stub!(:get_user_info).and_raise(StandardError) controller.stub!(:oauth_service).and_return(oauth_service) # Exercise get callback, {:oauth_verifier => xyz} flash[:error].should == Twitter API failure (account login) response.should redirect_to root_url endend
52. 52. A POSITIVE TESTit "redirects to the users page after a successful login" do user_info_stub = {id => 123456, screen_name => johndoe, profile_image_url => some_image_url} oauth_service_mock = mock(OauthService, :access_token => 12345, :access_secret => abcde) fake oauth_service_mock.should_receive(:get_user_info) .with(xyz) .and_return(user_info_stub) stub user = User.new dummy mock User.stub!(:save_user!).and_return(user) controller.stub!(:oauth_service).and_return(oauth_service_mock) # Exercise get callback, {:oauth_verifier => xyz} controller.current_user.should == user response.should redirect_to userend
53. 53. A POSITIVE TESTit "redirects to the users page after a successful login" do user_info_stub = {id => 123456, screen_name => johndoe, profile_image_url => some_image_url} oauth_service_mock = mock(OauthService, :access_token => 12345, :access_secret => abcde) fake oauth_service_mock.should_receive(:get_user_info) .with(xyz) .and_return(user_info_stub) stub user = User.new(:id => 122) dummy mock User.stub!(:save_user!).and_return(user) controller.stub!(:oauth_service).and_return(oauth_service_mock) # Exercise get callback, {:oauth_verifier => xyz} controller.current_user.should == user response.should redirect_to userend
54. 54. CHECK THE ARGUMENTit "redirects to the users page after a successful login" do user_info_stub = {id => 123456, screen_name => johndoe, profile_image_url => some_image_url} oauth_service_mock = mock(OauthService, :access_token => 12345, :access_secret => abcde) oauth_service_mock.should_receive(:get_user_info) .with(abc) .and_return(user_info_stub) user = User.new(:id => 122) User.stub!(:save_user!).and_return(user) controller.stub!(:oauth_service).and_return(oauth_service_mock) # Exercise get callback, {:oauth_verifier => xyz} controller.current_user.should == user response.should redirect_to userend
55. 55. OH, SNAP!it "redirects to the users page after a successful login" do user_info_stub = {id => 123456, screen_name => johndoe, profile_image_url => some_image_url} oauth_service_mock = mock(OauthService, :access_token => 12345, :access_secret => abcde) oauth_service_mock.should_receive(:get_user_info) .with(abc) .and_return(user_info_stub) user = User.new(:id => 122) User.stub!(:save_user!).and_return(user) controller.stub!(:oauth_service).and_return(oauth_service_mock) # Exercise get callback, {:oauth_verifier => xyz} controller.current_user.should == user response.should redirect_to userend
56. 56. AND THE CUCUMBER?