How To Test Everything
Upcoming SlideShare
Loading in...5
×
 

How To Test Everything

on

  • 4,166 views

Talk from WindyCityRails 2009

Talk from WindyCityRails 2009

Statistics

Views

Total Views
4,166
Views on SlideShare
4,151
Embed Views
15

Actions

Likes
7
Downloads
66
Comments
0

1 Embed 15

http://www.slideshare.net 15

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

How To Test Everything How To Test Everything Presentation Transcript

  • How To Test Everything Noel Rappin Pathfinder Development
  • How To Test Everything ... In Rails Noel Rappin Pathfinder Development
  • Guidelines
  • The First Guideline Any change to the logic of the program should be driven by a failed test
  • The Second Guideline A test should be as close as possible to the associated code.
  • The Third Guideline Test features and functionality, not code
  • The Fourth Guideline Rails works. (Mostly). You don’t need to test it.
  • Framework
  • What Framework To Use?
  • What Framework To Use? Short Answer: I don’t care
  • What Framework To Use? Short Answer: I don’t care Longer Answer: Start with Rails Core, move when you have an unfulfilled need
  • 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
  • Models
  • Associations
  • 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
  • Named Scopes
  • 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
  • Named Scope Tests
  • Named Scope Tests assert_same_elements [@melvin, @thomas], User.primary_status
  • 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
  • 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
  • Validations
  • 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
  • Controllers
  • Filters
  • 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
  • Views
  • Views Test for semantic structure (DOM ID, class) assert_select is your friend Sometimes, not test first
  • Test for the Negative What’s not there is as important as what is
  • Test for the Negative What’s not there is as important as what is assert_select "#edit_link", :count => 0
  • 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
  • Stupid assert_select Tricks
  • Stupid assert_select Tricks assert_select "input[name *= phone]"
  • Stupid assert_select Tricks assert_select "input[name *= phone]" assert_select "li#?", dom_id(@user, :item), :count => 1
  • 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
  • Helpers
  • 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
  • Testing Block Helpers
  • Testing Block Helpers def if_logged_in yield if logged_in? end
  • Testing Block Helpers def if_logged_in yield if logged_in? end <% if_logged_in do %> <%= link_to "logout", logout_path %> <% end %>
  • 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
  • Testing Output Helpers
  • Testing Output def make_headline Helpers concat("<h1 class='headline'>#{yield}</h1>") end
  • 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
  • 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
  • assert_select helpers
  • 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
  • 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
  • Email
  • 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
  • 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
  • User Interaction
  • 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
  • Ajax
  • Ajax assert_select_rjs, but only for Rails stuff test "an ajax call" xhr get :create assert_select_rjs :replace, :dom_id, 12 end
  • 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, ''); }); }); });
  • External Sites
  • Third Party Sites/ Web Mock, Mock, Mock, Mock Encapsulate call into an easily-mocked method Remember to test failure response
  • Rake
  • Rake Tasks Encapsulate the task into a class/ method and test that method
  • 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
  • Dates and Time
  • Date/Time Timecop
  • Date/Time Timecop setup :timecop_freeze teardown :timecop_return def timecop_freeze Timecop.freeze(Time.now) end def timecop_return Timecop.return end
  • 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)
  • File Upload
  • File Uploads fixture_file_upload post :update, :image => fixture_file_upload( '/test/fixtures/face.png', 'image/png')
  • Rack Rails Metal
  • 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
  • Legacy Applications
  • 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
  • Wrap Up
  • 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
  • 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/