How To Test Everything

2,976 views

Published on

Talk from WindyCityRails 2009

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

No Downloads
Views
Total views
2,976
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
68
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide
  • How To Test Everything

    1. 1. How To Test Everything Noel Rappin Pathfinder Development
    2. 2. How To Test Everything ... In Rails Noel Rappin Pathfinder Development
    3. 3. Guidelines
    4. 4. The First Guideline Any change to the logic of the program should be driven by a failed test
    5. 5. The Second Guideline A test should be as close as possible to the associated code.
    6. 6. The Third Guideline Test features and functionality, not code
    7. 7. The Fourth Guideline Rails works. (Mostly). You don’t need to test it.
    8. 8. Framework
    9. 9. What Framework To Use?
    10. 10. What Framework To Use? Short Answer: I don’t care
    11. 11. What Framework To Use? Short Answer: I don’t care Longer Answer: Start with Rails Core, move when you have an unfulfilled need
    12. 12. What Framework To Use? Short Answer: I don’t care Longer Answer: Start with Rails Core, move when you have an unfulfilled need Those needs: Contexts, Factories, Mocks
    13. 13. Models
    14. 14. Associations
    15. 15. Associations No need to just check for the existence of an association Associations should be driven by failing tests of actual functionality Code extensions in an association block should be treated like any other code
    16. 16. Named Scopes
    17. 17. Named Scopes Named scopes are methods Don’t test that a named scope has the SQL decorations you put in Do test that the scope correctly finds or manages the objects you expect
    18. 18. Named Scope Tests
    19. 19. Named Scope Tests assert_same_elements [@melvin, @thomas], User.primary_status
    20. 20. Named Scope Tests assert_same_elements [@melvin, @thomas], User.primary_status should_have_named_scope :primary_status do |u| ["online", "away"]include?(u.status) end
    21. 21. Should Have Scope def self.should_match_named_scope(named_scope, *args, &block) should "match named scope #{named_scope}" do ar_class = self.class.model_class scoped_objects = ar_class.send(named_scope, *args) assert !scoped_objects.blank? scoped_objects.each do |obj| assert block.call(obj) end non_scoped_objects = ar_class.all - scoped_objects assert !non_scoped_objects.blank? non_scoped_objects.each do |obj| assert !block.call(obj) end end end
    22. 22. Validations
    23. 23. Validations Don’t test Rails code Do test for valid state Do test anything custom And anything with a regex Failing saves can lead to irritating test
    24. 24. Controllers
    25. 25. Filters
    26. 26. Filters Generally, test as part of actual action Don’t need to re-test refactors Failing filters are a big cause of silent test failures
    27. 27. Views
    28. 28. Views Test for semantic structure (DOM ID, class) assert_select is your friend Sometimes, not test first
    29. 29. Test for the Negative What’s not there is as important as what is
    30. 30. Test for the Negative What’s not there is as important as what is assert_select "#edit_link", :count => 0
    31. 31. Test for the Negative What’s not there is as important as what is assert_select "#edit_link", :count => 0 assert_select "li:not(#edit_link)", :count => 2
    32. 32. Stupid assert_select Tricks
    33. 33. Stupid assert_select Tricks assert_select "input[name *= phone]"
    34. 34. Stupid assert_select Tricks assert_select "input[name *= phone]" assert_select "li#?", dom_id(@user, :item), :count => 1
    35. 35. Stupid assert_select Tricks assert_select "input[name *= phone]" assert_select "li#?", dom_id(@user, :item), :count => 1 assert_select "ul#directory_list" do assert_select "li:nth-of-type(1)", :text => "Albert Aardvark" assert_select "li:nth-of-type(2)", :text => "Zack Zebra" end
    36. 36. Helpers
    37. 37. Helpers DO TEST HELPERS Auto generated in Rails 2.3 and up test/unit/helpers Methods like url_for can be stubbed in the test class class UsersHelperTest < ActionView::TestCase end
    38. 38. Testing Block Helpers
    39. 39. Testing Block Helpers def if_logged_in yield if logged_in? end
    40. 40. Testing Block Helpers def if_logged_in yield if logged_in? end <% if_logged_in do %> <%= link_to "logout", logout_path %> <% end %>
    41. 41. Testing Block Helpers def if_logged_in yield if logged_in? end <% if_logged_in do %> <%= link_to "logout", logout_path %> <% end %> test "logged_in" do assert !logged_in? assert_nil(if_logged_in {"logged in"}) login_as users(:quentin) assert logged_in? assert_equal("logged in", if_logged_in {"logged in"}) end
    42. 42. Testing Output Helpers
    43. 43. Testing Output def make_headline Helpers concat("<h1 class='headline'>#{yield}</h1>") end
    44. 44. Testing Output def make_headline Helpers concat("<h1 class='headline'>#{yield}</h1>") end test "make headline" do assert_dom_equal("<h1 class='headline'>fred</h1>", make_headline { "fred" }) end
    45. 45. Testing Output def make_headline Helpers concat("<h1 class='headline'>#{yield}</h1>") end test "make headline" do assert_dom_equal("<h1 class='headline'>fred</h1>", make_headline { "fred" }) end test "make headline with output buffer" do make_headline { "fred" } assert_dom_equal("<h1 class='headline'>fred</h1>", output_buffer) end
    46. 46. assert_select helpers
    47. 47. assert_select helpers setup :setup_response def setup_response @output_buffer = "" @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end def make_response(text) @response.body = text end
    48. 48. assert_select helpers setup :setup_response def setup_response @output_buffer = "" @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end def make_response(text) @response.body = text end test "make headline with response body" do make_headline { "fred" } make_response output_buffer assert_select("h1.headline") end
    49. 49. Email
    50. 50. Email ActionMailer::Base.deliveries.clear Treat emails like views assert_select_email email_spec plugin for Cucumber & RSpec Shoulda: assert_did_not_sent_email, assert_sent_email
    51. 51. Email ActionMailer::Base.deliveries.clear Treat emails like views assert_select_email email_spec plugin for Cucumber & RSpec Shoulda: assert_did_not_sent_email, do should "send an email to mom" assert_sent_email assert_sent_email do |email| email.to == "mom@mommy.com" end end
    52. 52. User Interaction
    53. 53. Multi-User Interaction Integration tests test "user interaction" do my_session = open_session your_session = open_session my_session.post("messages/send", :to => you) your_session.get("messages/show") assert_equal 1, your_session.assigns(:messages).size end
    54. 54. Ajax
    55. 55. Ajax assert_select_rjs, but only for Rails stuff test "an ajax call" xhr get :create assert_select_rjs :replace, :dom_id, 12 end
    56. 56. Blue Ridge / Screw.Unit Screw.Unit(function() { describe("With my search box and default", function() { it("should switch the default", function() { search_focus($('#search')); expect($('#search').attr('value')).to(equal, ''); }); }); });
    57. 57. External Sites
    58. 58. Third Party Sites/ Web Mock, Mock, Mock, Mock Encapsulate call into an easily-mocked method Remember to test failure response
    59. 59. Rake
    60. 60. Rake Tasks Encapsulate the task into a class/ method and test that method
    61. 61. Rake Tasks Encapsulate the task into a class/ method and test that method test "my rake task" do @rake = Rake::Application.new Rake.application = @rake Rake.application.rake_require "lib/tasks/app" Rake::Task.define_task(:environment) @rake[:task_name].invoke end http:// www.philsergi.com
    62. 62. Dates and Time
    63. 63. Date/Time Timecop
    64. 64. Date/Time Timecop setup :timecop_freeze teardown :timecop_return def timecop_freeze Timecop.freeze(Time.now) end def timecop_return Timecop.return end
    65. 65. Date/Time Timecop setup :timecop_freeze teardown :timecop_return def timecop_freeze Timecop.freeze(Time.now) end def timecop_return Timecop.return end Timecop.freeze(Date.parse('8 April 2009').to_time)
    66. 66. File Upload
    67. 67. File Uploads fixture_file_upload post :update, :image => fixture_file_upload( '/test/fixtures/face.png', 'image/png')
    68. 68. Rack Rails Metal
    69. 69. Rails Metal Rack Middleware Integration tests and cucumber Rack::Test def test_redirect_logged_in_users_to_dashboard authorize "bryan", "secret" get "/" follow_redirect! assert_equal "http://example.org/redirected", last_request.url assert last_response.ok? end
    70. 70. Legacy Applications
    71. 71. Legacy Apps Acceptance is the first step Make sure the suite runs Try black-box tests Try mock objects Look for seams Don’t look backward
    72. 72. Wrap Up
    73. 73. Wrap Up Code should flow from failed tests Test features and functionality Look for tools to help The goal is great code that delivers value
    74. 74. For More Info http://speakerrate.com/events/183 http://www.pathf.com/blogs http://blog.railsrx.com http://www.twitter.com/noelrap http://www.pragprog.com/titles/

    ×