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 un...
What Framework To
         Use?
Short Answer: I don’t care

Longer Answer: Start with Rails Core,
move when you have an un...
Models
Associations
Associations
No need to just check for the existence
of an association

Associations should be driven by
failing tests of ...
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...
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_sta...
Should Have Scope
def self.should_match_named_scope(named_scope,
   *args, &block)
 should "match named scope #{named_scop...
Validations
Validations

Don’t test Rails code

Do test for valid state

Do test anything custom

And anything with a regex

Failing s...
Controllers
Filters
Filters

Generally, test as part of actual action

Don’t need to re-test refactors

Failing filters are a big cause of sile...
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...
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_i...
Stupid assert_select
            Tricks
                       assert_select "input[name *= phone]"


   assert_select "li...
Helpers
Helpers

DO TEST HELPERS

Auto generated in Rails 2.3 and up

test/unit/helpers

Methods like url_for can be stubbed in
th...
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 %>
           ...
Testing Block Helpers
def if_logged_in
 yield if logged_in?
end


                       <% if_logged_in do %>
           ...
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 h...
Testing Output
def make_headline
                  Helpers
 concat("<h1 class='headline'>#{yield}</h1>")
end

test "make h...
assert_select helpers
assert_select helpers
setup :setup_response
def setup_response
 @output_buffer = ""
 @request = ActionController::TestRequ...
assert_select helpers
setup :setup_response
def setup_response
 @output_buffer = ""
 @request = ActionController::TestRequ...
Email
Email
           ActionMailer::Base.deliveries.clear

Treat emails like views

assert_select_email

email_spec plugin for ...
Email
             ActionMailer::Base.deliveries.clear

Treat emails like views

assert_select_email

email_spec plugin fo...
User
Interaction
Multi-User Interaction
Integration tests
 test "user interaction" do
  my_session = open_session
  your_session = open_ses...
Ajax
Ajax



assert_select_rjs, but only for Rails
stuff
      test "an ajax call"
       xhr get :create
       assert_select_...
Blue Ridge /
                 Screw.Unit
Screw.Unit(function() {
  describe("With my search box and default", function() {...
External Sites
Third Party Sites/ Web

Mock, Mock, Mock, Mock

Encapsulate call into an easily-mocked
method

Remember to test failure re...
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::...
Dates and
  Time
Date/Time
Timecop
Date/Time
Timecop
          setup :timecop_freeze
          teardown :timecop_return

          def timecop_freeze
       ...
Date/Time
Timecop
            setup :timecop_freeze
            teardown :timecop_return

            def timecop_freeze
 ...
File Upload
File Uploads



fixture_file_upload
post :update,
   :image => fixture_file_upload(
       '/test/fixtures/face.png', 'image/pn...
Rack
Rails Metal
Rails Metal
         Rack Middleware
Integration tests and cucumber

Rack::Test
def test_redirect_logged_in_users_to_dashb...
Legacy
Applications
Legacy Apps
Acceptance is the first step

Make sure the suite runs

Try black-box tests

Try mock objects

Look for seams

...
Wrap Up
Wrap Up

Code should flow from failed tests

Test features and functionality

Look for tools to help

The goal is great cod...
For More Info
http://speakerrate.com/events/183

http://www.pathf.com/blogs

http://blog.railsrx.com

http://www.twitter.c...
Upcoming SlideShare
Loading in...5
×

How To Test Everything

2,654

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,654
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
67
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/
    1. A particular slide catching your eye?

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

    ×