Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 4931

Show
Ignore:
Timestamp:
09/03/06 20:18:06 (2 years ago)
Author:
david
Message:

Better compartmentalizing of assertions

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/lib/action_controller/assertions.rb

    r4929 r4931  
    11require 'test/unit' 
    22require 'test/unit/assertions' 
    3 require 'rexml/document' 
    4 require File.dirname(__FILE__) + "/vendor/html-scanner/html/document" 
     3 
     4module ActionController #:nodoc: 
     5  # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions 
     6  # can be used against. These collections are: 
     7  # 
     8  # * assigns: Instance variables assigned in the action that are available for the view. 
     9  # * session: Objects being saved in the session. 
     10  # * flash: The flash objects currently in the session. 
     11  # * cookies: Cookies being sent to the user on this request. 
     12  #  
     13  # These collections can be used just like any other hash: 
     14  # 
     15  #   assert_not_nil assigns(:person) # makes sure that a @person instance variable was set 
     16  #   assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave" 
     17  #   assert flash.empty? # makes sure that there's nothing in the flash 
     18  # 
     19  # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To 
     20  # appease our yearning for symbols, though, an alternative accessor has been deviced using a method call instead of index referencing. 
     21  # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work. 
     22  # 
     23  # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url. 
     24  # 
     25  # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another 
     26  # action call which can then be asserted against. 
     27  # 
     28  # == Manipulating the request collections 
     29  # 
     30  # The collections described above link to the response, so you can test if what the actions were expected to do happened. But 
     31  # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions 
     32  # and cookies, though. For sessions, you just do: 
     33  # 
     34  #   @request.session[:key] = "value" 
     35  # 
     36  # For cookies, you need to manually create the cookie, like this: 
     37  # 
     38  #   @request.cookies["key"] = CGI::Cookie.new("key", "value") 
     39  # 
     40  # == Testing named routes 
     41  # 
     42  # If you're using named routes, they can be easily tested using the original named routes methods straight in the test case. 
     43  # Example:  
     44  # 
     45  #  assert_redirected_to page_url(:title => 'foo') 
     46  module Assertions 
     47  end 
     48end 
     49 
     50require File.dirname(__FILE__) + '/assertions/assert_response' 
     51require File.dirname(__FILE__) + '/assertions/assert_select' 
     52require File.dirname(__FILE__) + '/assertions/assert_tag' 
     53require File.dirname(__FILE__) + '/assertions/assert_dom' 
     54require File.dirname(__FILE__) + '/assertions/assert_routing' 
     55require File.dirname(__FILE__) + '/assertions/assert_valid' 
    556 
    657module Test #:nodoc: 
    758  module Unit #:nodoc: 
    8     # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions 
    9     # can be used against. These collections are: 
    10     # 
    11     # * assigns: Instance variables assigned in the action that are available for the view. 
    12     # * session: Objects being saved in the session. 
    13     # * flash: The flash objects currently in the session. 
    14     # * cookies: Cookies being sent to the user on this request. 
    15     #  
    16     # These collections can be used just like any other hash: 
    17     # 
    18     #   assert_not_nil assigns(:person) # makes sure that a @person instance variable was set 
    19     #   assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave" 
    20     #   assert flash.empty? # makes sure that there's nothing in the flash 
    21     # 
    22     # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To 
    23     # appease our yearning for symbols, though, an alternative accessor has been deviced using a method call instead of index referencing. 
    24     # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work. 
    25     # 
    26     # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url. 
    27     # 
    28     # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another 
    29     # action call which can then be asserted against. 
    30     # 
    31     # == Manipulating the request collections 
    32     # 
    33     # The collections described above link to the response, so you can test if what the actions were expected to do happened. But 
    34     # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions 
    35     # and cookies, though. For sessions, you just do: 
    36     # 
    37     #   @request.session[:key] = "value" 
    38     # 
    39     # For cookies, you need to manually create the cookie, like this: 
    40     # 
    41     #   @request.cookies["key"] = CGI::Cookie.new("key", "value") 
    42     # 
    43     # == Testing named routes 
    44     # 
    45     # If you're using named routes, they can be easily tested using the original named routes methods straight in the test case. 
    46     # Example:  
    47     # 
    48     #  assert_redirected_to page_url(:title => 'foo') 
    49     module Assertions 
    50       # Asserts that the response is one of the following types: 
    51       #  
    52       # * <tt>:success</tt>: Status code was 200 
    53       # * <tt>:redirect</tt>: Status code was in the 300-399 range 
    54       # * <tt>:missing</tt>: Status code was 404 
    55       # * <tt>:error</tt>:  Status code was in the 500-599 range 
    56       # 
    57       # You can also pass an explicit status code number as the type, like assert_response(501) 
    58       def assert_response(type, message = nil) 
    59         clean_backtrace do 
    60           if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?") 
    61             assert_block("") { true } # to count the assertion 
    62           elsif type.is_a?(Fixnum) && @response.response_code == type 
    63             assert_block("") { true } # to count the assertion 
    64           else 
    65             assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false } 
    66           end                
    67         end 
    68       end 
     59    class TestCase #:nodoc: 
     60      include ActionController::Assertions::ResponseAssertions 
     61      include ActionController::Assertions::SelectorAssertions 
     62      include ActionController::Assertions::RoutingAssertions 
     63      include ActionController::Assertions::TagAssertions 
     64      include ActionController::Assertions::DomAssertions 
     65      include ActionController::Assertions::ModelAssertions 
    6966 
    70       # Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, 
    71       # such that assert_redirected_to(:controller => "weblog") will also match the redirection of  
    72       # redirect_to(:controller => "weblog", :action => "show") and so on. 
    73       def assert_redirected_to(options = {}, message=nil) 
    74         clean_backtrace do 
    75           assert_response(:redirect, message) 
    76           return true if options == @response.redirected_to 
    77           ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? 
    78  
    79           begin 
    80             url  = {} 
    81             original = { :expected => options, :actual => @response.redirected_to.is_a?(Symbol) ? @response.redirected_to : @response.redirected_to.dup } 
    82             original.each do |key, value| 
    83               if value.is_a?(Symbol) 
    84                 value = @controller.respond_to?(value, true) ? @controller.send(value) : @controller.send("hash_for_#{value}_url") 
    85               end 
    86  
    87               unless value.is_a?(Hash) 
    88                 request = case value 
    89                   when NilClass    then nil 
    90                   when /^\w+:\/\// then recognized_request_for(%r{^(\w+://.*?(/|$|\?))(.*)$} =~ value ? $3 : nil) 
    91                   else                  recognized_request_for(value) 
    92                 end 
    93                 value = request.path_parameters if request 
    94               end 
    95  
    96               if value.is_a?(Hash) # stringify 2 levels of hash keys 
    97                 if name = value.delete(:use_route) 
    98                   route = ActionController::Routing::Routes.named_routes[name] 
    99                   value.update(route.parameter_shell) 
    100                 end 
    101  
    102                 value.stringify_keys! 
    103                 value.values.select { |v| v.is_a?(Hash) }.collect { |v| v.stringify_keys! } 
    104                 if key == :expected && value['controller'] == @controller.controller_name && original[:actual].is_a?(Hash) 
    105                   original[:actual].stringify_keys! 
    106                   value.delete('controller') if original[:actual]['controller'].nil? || original[:actual]['controller'] == value['controller'] 
    107                 end 
    108               end 
    109  
    110               if value.respond_to?(:[]) && value['controller'] 
    111                 if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/') 
    112                   value['controller'] = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)  
    113                 end 
    114                 value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash 
    115               end 
    116               url[key] = value 
    117             end 
    118              
    119  
    120             @response_diff = url[:expected].diff(url[:actual]) if url[:actual] 
    121             msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is <?>), difference: <?>",  
    122                                 url[:actual], @response_diff) 
    123              
    124             assert_block(msg) do 
    125               url[:expected].keys.all? do |k| 
    126                 if k == :controller then url[:expected][k] == ActionController::Routing.controller_relative_to(url[:actual][k], @controller.class.controller_path) 
    127                 else parameterize(url[:expected][k]) == parameterize(url[:actual][k]) 
    128                 end 
    129               end 
    130             end 
    131           rescue ActionController::RoutingError # routing failed us, so match the strings only. 
    132             msg = build_message(message, "expected a redirect to <?>, found one to <?>", options, @response.redirect_url) 
    133             url_regexp = %r{^(\w+://.*?(/|$|\?))(.*)$} 
    134             eurl, epath, url, path = [options, @response.redirect_url].collect do |url| 
    135               u, p = (url_regexp =~ url) ? [$1, $3] : [nil, url] 
    136               [u, (p.first == '/') ? p : '/' + p] 
    137             end.flatten 
    138  
    139             assert_equal(eurl, url, msg) if eurl && url 
    140             assert_equal(epath, path, msg) if epath && path  
    141           end 
    142         end 
    143       end 
    144  
    145       # Asserts that the request was rendered with the appropriate template file. 
    146       def assert_template(expected = nil, message=nil) 
    147         clean_backtrace do 
    148           rendered = expected ? @response.rendered_file(!expected.include?('/')) : @response.rendered_file 
    149           msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered) 
    150           assert_block(msg) do 
    151             if expected.nil? 
    152               !@response.rendered_with_file? 
    153             else 
    154               expected == rendered 
    155             end 
    156           end                
    157         end 
    158       end 
    159  
    160       # Asserts that the routing of the given path was handled correctly and that the parsed options match. 
    161       # 
    162       #   assert_recognizes({:controller => 'items', :action => 'index'}, 'items') 
    163       # 
    164       # Pass a hash in the second argument to specify the request method.  This is useful for routes 
    165       # requiring a specific method. 
    166       # 
    167       #   assert_recognizes({:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post}) 
    168       # 
    169       def assert_recognizes(expected_options, path, extras={}, message=nil) 
    170         if path.is_a? Hash 
    171           request_method = path[:method] 
    172           path           = path[:path] 
    173         else 
    174           request_method = nil 
    175         end 
    176  
    177         clean_backtrace do  
    178           ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?  
    179           request = recognized_request_for(path, request_method) 
    180        
    181           expected_options = expected_options.clone 
    182           extras.each_key { |key| expected_options.delete key } unless extras.nil? 
    183        
    184           expected_options.stringify_keys! 
    185           routing_diff = expected_options.diff(request.path_parameters) 
    186           msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",  
    187               request.path_parameters, expected_options, expected_options.diff(request.path_parameters)) 
    188           assert_block(msg) { request.path_parameters == expected_options } 
    189         end 
    190       end 
    191  
    192       # Asserts that the provided options can be used to generate the provided path. 
    193       def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil) 
    194         clean_backtrace do  
    195           expected_path = "/#{expected_path}" unless expected_path[0] == ?/ 
    196           # Load routes.rb if it hasn't been loaded. 
    197           ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?  
    198        
    199           generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, extras) 
    200           found_extras = options.reject {|k, v| ! extra_keys.include? k} 
    201  
    202           msg = build_message(message, "found extras <?>, not <?>", found_extras, extras) 
    203           assert_block(msg) { found_extras == extras } 
    204        
    205           msg = build_message(message, "The generated path <?> did not match <?>", generated_path,  
    206               expected_path) 
    207           assert_block(msg) { expected_path == generated_path } 
    208         end 
    209       end 
    210  
    211       # Asserts that path and options match both ways; in other words, the URL generated from  
    212       # options is the same as path, and also that the options recognized from path are the same as options 
    213       def assert_routing(path, options, defaults={}, extras={}, message=nil) 
    214         assert_recognizes(options, path, extras, message) 
    215          
    216         controller, default_controller = options[:controller], defaults[:controller]  
    217         if controller && controller.include?(?/) && default_controller && default_controller.include?(?/) 
    218           options[:controller] = "/#{controller}" 
    219         end 
    220           
    221         assert_generates(path, options, defaults, extras, message) 
    222       end 
    223  
    224       # test 2 html strings to be equivalent, i.e. identical up to reordering of attributes 
    225       def assert_dom_equal(expected, actual, message="") 
    226         clean_backtrace do 
    227           expected_dom = HTML::Document.new(expected).root 
    228           actual_dom = HTML::Document.new(actual).root 
    229           full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s) 
    230           assert_block(full_message) { expected_dom == actual_dom } 
    231         end 
    232       end 
    233        
    234       # negated form of +assert_dom_equivalent+ 
    235       def assert_dom_not_equal(expected, actual, message="") 
    236         clean_backtrace do 
    237           expected_dom = HTML::Document.new(expected).root 
    238           actual_dom   = HTML::Document.new(actual).root 
    239           full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s) 
    240           assert_block(full_message) { expected_dom != actual_dom } 
    241         end 
    242       end 
    243  
    244       # ensures that the passed record is valid by active record standards. returns the error messages if not 
    245       def assert_valid(record) 
    246         clean_backtrace do 
    247           assert record.valid?, record.errors.full_messages.join("\n") 
    248         end 
    249       end              
    250        
    25167      def clean_backtrace(&block) 
    25268        yield 
     
    25571        raise AssertionFailedError, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ } 
    25672      end 
    257        
    258       private 
    259         def recognized_request_for(path, request_method = nil) 
    260           path = "/#{path}" unless path.first == '/' 
    261  
    262           # Assume given controller 
    263           request = ActionController::TestRequest.new({}, {}, nil) 
    264           request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method 
    265           request.path   = path 
    266  
    267           ActionController::Routing::Routes.recognize(request) 
    268           request 
    269         end 
    270          
    271         def parameterize(value) 
    272           value.respond_to?(:to_param) ? value.to_param : value 
    273         end 
    27473    end 
    27574  end 
  • trunk/actionpack/lib/action_controller/assertions/assert_select.rb

    r4929 r4931  
    77require 'test/unit/assertions' 
    88require 'rexml/document' 
    9 require File.dirname(__FILE__) + "/vendor/html-scanner/html/document" 
     9require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document" 
    1010 
    1111 
     
    554554  end 
    555555end 
    556  
    557 Test::Unit::TestCase.send :include, ActionController::Assertions::SelectorAssertions 
  • trunk/actionpack/lib/action_controller/assertions/assert_tag.rb

    r4929 r4931  
    22require 'test/unit/assertions' 
    33require 'rexml/document' 
    4 require File.dirname(__FILE__) + "/vendor/html-scanner/html/document" 
     4require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document" 
    55 
    66module ActionController 
     
    118118  end 
    119119end 
    120  
    121 Test::Unit::TestCase.send :include, ActionController::Assertions::TagAssertions 
  • trunk/actionpack/lib/action_controller/test_process.rb

    r4929 r4931  
    1 require File.dirname(__FILE__) + '/assertions' 
    2 require File.dirname(__FILE__) + '/assert_select' 
    3 require File.dirname(__FILE__) + '/assert_tag' 
    4 require File.dirname(__FILE__) + '/deprecated_assertions' 
     1begin 
     2  require File.dirname(__FILE__) + '/assertions' 
     3  require File.dirname(__FILE__) + '/deprecated_assertions' 
     4rescue Exception => e 
     5  print e 
     6end 
    57 
    68module ActionController #:nodoc: 
  • trunk/actionpack/test/controller/assert_select_test.rb

    r4929 r4931  
    77require File.dirname(__FILE__) + '/fake_controllers' 
    88require "action_mailer" 
     9 
    910 
    1011class AssertSelectTest < Test::Unit::TestCase