0
MOCKS VS STUBS  - How will we test this?
WHO AM I?•   Attila Domokos•   .NET Programmer•   Ruby Enthusiast•   Twitter: @adomokos•   Blog: http://www.adomokos.com• ...
WHO...??
MOCKS AREN’T STUBS
THE WAREHOUSE EXAMPLE
CALL SEQUENCES
describe Order do  before(:each) do    @warehouse = Warehouse.new    @warehouse.add(:Cleveland, 50)    @warehouse.add(:Akr...
USING A MOCKdescribe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland, 50)...
USING A MOCKdescribe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland, 50)...
USING A STUB - VERBOSEdescribe Order do  it "fills the order with enough items in warehouse" do    @warehouse.stub!(:quant...
describe Order do  before(:each) do    @warehouse = stub(warehouse_stub)    setup_stub  end it "fills the order with enoug...
HOW COOL IS THAT?describe Order do  before(:each) do    @warehouse = stub(warehouse_stub,         {:quantity_at => 50, :se...
CAN WE DO THE SAME?describe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Clevela...
OH, SNAP!describe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland, 50)   ...
BUT THIS WORKS!describe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland, ...
BUT THIS WORKS!describe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland, ...
SO WHAT’S THE DIFFERENCE?
NO, SERIOUSLY!•   Stubs - Provide canned responses to messages•   Mocks - Specify AND verify that certain messages should ...
MOCKS CAN DO MORE!Expecting Messages:  my_mock.should_receive(:msg).once  my_mock.should_not_receive(:msg).onceExpecting A...
MOCKS CAN DO MOREdescribe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland...
MOCKS CAN DO MOREdescribe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland...
CHANGE THE ORDERdescribe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland,...
OH, SNAP!describe Order do  it "fills the order with enough items in warehouse" do    order = Order.new(:Cleveland, 50)   ...
I USED RSPEC MOCKING...     ... but there are other libraries:•   Flexmock•   Mocha•   rr
FLEXMOCK - TEST/UNITclass TestOrder < Test::Unit::TestCase  def test_fills_order_with_enough_items_at_location    order = ...
FLEXMOCK - TEST/UNITclass TestOrder < Test::Unit::TestCase  def test_fills_order_with_enough_items_at_location    order = ...
WHEN TO USE MOCKS/STUBS?•   Using an external service•   Verifying a protocol•   Objects are complicated to create
WHEN TO USE MOCKS/STUBS?•   Using an external service•   Verifying a protocol•   Objects are complicated to create
BUT HOWCAN I USE  THIS?
MEET TWEETSTATS
TWITTER OAUTH
LOGGED ON
SEND TWEET
VIEW FRIENDS
VIEW FOLLOWERS
ENTER THE SERVICEModel         ControllerView                      OauthService
THE USERS CONTROLLERclass UsersController < ApplicationController  before_filter :oauth_login_required,                 :e...
STUB IT OUT!describe UsersController do  TEST_URL = twitter_test  describe "GET new" do    it "redirects to twitter - stub...
STUB IT OUT!describe UsersController do  TEST_URL = twitter_test  describe "GET new" do    it "redirects to twitter - stub...
OR MOCK ITdescribe UsersController do  TEST_URL = twitter_test.com  describe "GET new" do    it "redirects to twitter - st...
OR MOCK ITdescribe UsersController do  TEST_URL = twitter_test.com  describe "GET new" do    it "redirects to twitter - st...
BUT WHAT’S THE DIFFERENCE?                            Unused Message Stuboauth_service_stub =    stub(:login_by_oauth => T...
THIS WILL FAILit "redirects to twitter - mock example" do  oauth_service_mock = mock(:oauth_service)  oauth_service_mock  ...
OH, SNAP!it "redirects to twitter - mock example" do  oauth_service_mock = mock(:oauth_service)  oauth_service_mock       ...
OK, I AM AUTHENTICATEDdef callback  user_info =    self.oauth_service.get_user_info(params[:oauth_verifier])  @user = User...
A NEGATIVE TESTdescribe "Get callback" do  it "redirects to the home page when error occurs in service" do    # I can use ...
A NEGATIVE TESTdescribe "Get callback" do  it "redirects to the home page when error occurs in service" do    # I can use ...
OR EXPECT IT TWICEdescribe "Get callback" do  it "redirects to the home page when error occurs in service" do    # I can u...
OH, SNAP!describe "Get callback" do  it "redirects to the home page when error occurs in service" do    # I can use a mock...
A STUB WILL BE FINEdescribe "Get callback" do  it "redirects to the home page when error occurs in service" do    # I coul...
A STUB WILL BE FINEdescribe "Get callback" do  it "redirects to the home page when error occurs in service" do    # I coul...
A POSITIVE TESTit "redirects to the users page after a successful login" do  user_info_stub = {id => 123456, screen_name =...
A POSITIVE TESTit "redirects to the users page after a successful login" do  user_info_stub = {id => 123456, screen_name =...
CHECK THE ARGUMENTit "redirects to the users page after a successful login" do  user_info_stub = {id => 123456, screen_nam...
OH, SNAP!it "redirects to the users page after a successful login" do  user_info_stub = {id => 123456, screen_name => john...
AND THE CUCUMBER?
USER LOGIN FEATUREFeature: Login Users  In order to access the full site  As a user  I want to log on to the site with my ...
USER LOGIN FEATUREFeature: Login Users  In order to access the full site  As a user  I want to log on to the site with my ...
WHAT OPTIONS DO I HAVE?•   Hit Twitter every time•   Stub out my service object•   Stub out HTTP
WHAT OPTIONS DO I HAVE?•   Hit Twitter every time•   Stub out my service object•   Stub out HTTP                Run test o...
USER_LOGIN_STEPS.RB  Scenario: Login via Twitter    Given I am on the home page    When I follow "sign_in_with_twitter"   ...
ENV.RBrequire fakewebFakeWeb.register_uri(:get,          http://twitter.com/oauth/authorize?oauth_token=fake,          :bo...
NEVER GO OUTSIDE!require fakewebFakeWeb.allow_net_connect = falseFakeWeb.register_uri(:get,          http://twitter.com/oa...
VIEW FRIENDS
FRIENDS FEATUREFeature: View Friends  In order to see who my friends are  As a user  I want to go to the friends page and ...
THE STEPSGiven /^I am logged on to the site$/ do  visit callback_users_path                     view_friends_steps.rbend  ...
CHUCK NORRIS IS HAPPY!
RECAP• Stubs    are “Yugos”, Mocks are “Aston Martins”• Stubs    are canned answered to messages• Mocks     can verify:   ...
MOCKS CAN DO MORE!Expecting Messages:  my_mock.should_receive(:msg).once  my_mock.should_not_receive(:msg).onceExpecting A...
THANK YOU!
REFERENCES•   http://keremerkan.net/qr-code-and-2d-code-generator•   http://martinfowler.com/articles/mocksArentStubs.html...
Mock vs Stubs CleRB Presentation
Mock vs Stubs CleRB Presentation
Upcoming SlideShare
Loading in...5
×

Mock vs Stubs CleRB Presentation

2,169

Published on

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

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,169
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
32
Comments
0
Likes
3
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
  • Transcript of "Mock vs Stubs CleRB Presentation"

    1. 1. MOCKS VS STUBS - How will we test this?
    2. 2. WHO AM I?• Attila Domokos• .NET Programmer• Ruby Enthusiast• Twitter: @adomokos• Blog: http://www.adomokos.com• Email:
    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
    19. 19. MOCKS CAN DO MORE!Expecting Messages: my_mock.should_receive(:msg).once my_mock.should_not_receive(:msg).onceExpecting Arguments: my_mock.should_receive(:msg).with(1, 2) my_mock.should_receive(:msg).with(no_args()) my_mock.should_receive(:msg).with(any_args())Argument Constraints: my_mock.should_receive(:msg).with(1, an_instance_of(Fixnum), “b”) my_mock.should_receive(:msg).with(a, boolean(), b) my_mock.should_receive(:msg).with(/abc/)Receive Counts: my_mock.should_receive(:msg).once my_mock.should_receive(:msg).twice my_mock.should_receive(:msg).at_least(:once) my_mock.should_receive(:msg).at_most(:twice) my_mock.should_receive(:msg).any_number_of_timesOrdering: my_mock.should_receive(:one).ordered my_mock.should_receive(:two).ordered
    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
    31. 31. TWITTER OAUTH
    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?
    57. 57. USER LOGIN FEATUREFeature: Login Users In order to access the full site As a user I want to log on to the site with my twitter account Scenario: Login via Twitter Given I am on the home page When I follow "sign_in_with_twitter" And Twitter authorizes me Then I should see "Welcome back!"
    58. 58. USER LOGIN FEATUREFeature: Login Users In order to access the full site As a user I want to log on to the site with my twitter account Scenario: Login via Twitter Given I am on the home page When I follow "sign_in_with_twitter" And Twitter authorizes me Then I should see "Welcome back!"
    59. 59. WHAT OPTIONS DO I HAVE?• Hit Twitter every time• Stub out my service object• Stub out HTTP
    60. 60. WHAT OPTIONS DO I HAVE?• Hit Twitter every time• Stub out my service object• Stub out HTTP Run test offline mode
    61. 61. USER_LOGIN_STEPS.RB Scenario: Login via Twitter Given I am on the home page When I follow "sign_in_with_twitter" And Twitter authorizes me Then I should see "Welcome back!" When /^Twitter authorizes me$/ do visit callback_users_path end
    62. 62. ENV.RBrequire fakewebFakeWeb.register_uri(:get, http://twitter.com/oauth/authorize?oauth_token=fake, :body => success)FakeWeb.register_uri(:post, http://twitter.com/oauth/access_token, :body => oauth_token=fake&oauth_token_secret=fake)FakeWeb.register_uri(:get, http://twitter.com/account/verify_credentials.json, :response => File.join(::Rails.root.to_s, features, fixtures, verify_credentials.json))
    63. 63. NEVER GO OUTSIDE!require fakewebFakeWeb.allow_net_connect = falseFakeWeb.register_uri(:get, http://twitter.com/oauth/authorize?oauth_token=fake, :body => success)FakeWeb.register_uri(:post, http://twitter.com/oauth/access_token, :body => oauth_token=fake&oauth_token_secret=fake)FakeWeb.register_uri(:get, http://twitter.com/account/verify_credentials.json, :response => File.join(::Rails.root.to_s, features, fixtures, verify_credentials.json))
    64. 64. VIEW FRIENDS
    65. 65. FRIENDS FEATUREFeature: View Friends In order to see who my friends are As a user I want to go to the friends page and see my friends Background: Given I am logged on to the site Scenario: View friends When I follow "friends" Then I should see "You are following 4 friends."
    66. 66. THE STEPSGiven /^I am logged on to the site$/ do visit callback_users_path view_friends_steps.rbend $: curl -is http://twitter.com/statuses/friends.json? screen_name=adomokos&cursor=-1 > features/fixtures/friends.jsonFakeWeb.register_uri(:get, http://twitter.com/statuses/friends.json?screen_name=adomokos&cursor=-1, :response => File.join(::Rails.root.to_s, features, fixtures, friends.json)) env.rb
    67. 67. CHUCK NORRIS IS HAPPY!
    68. 68. RECAP• Stubs are “Yugos”, Mocks are “Aston Martins”• Stubs are canned answered to messages• Mocks can verify: • If message was sent or not • Input parameters • Order of message• Never go outside of your “box” during automated tests!
    69. 69. MOCKS CAN DO MORE!Expecting Messages: my_mock.should_receive(:msg).once my_mock.should_not_receive(:msg).onceExpecting Arguments: my_mock.should_receive(:msg).with(1, 2) my_mock.should_receive(:msg).with(no_args()) my_mock.should_receive(:msg).with(any_args())Argument Constraints: my_mock.should_receive(:msg).with(1, an_instance_of(Fixnum), “b”) my_mock.should_receive(:msg).with(a, boolean(), b) my_mock.should_receive(:msg).with(/abc/)Receive Counts: my_mock.should_receive(:msg).once my_mock.should_receive(:msg).twice my_mock.should_receive(:msg).at_least(:once) my_mock.should_receive(:msg).at_most(:twice) my_mock.should_receive(:msg).any_number_of_timesOrdering: my_mock.should_receive(:one).ordered my_mock.should_receive(:two).ordered
    70. 70. THANK YOU!
    71. 71. REFERENCES• http://keremerkan.net/qr-code-and-2d-code-generator• http://martinfowler.com/articles/mocksArentStubs.html• http://blog.zerosum.org/2009/7/13/twitter-auth-integration- testing• http://dev.twitter.com/console• http://rspec.info/documentation/mocks/ message_expectations.html
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×