module Shoulda # :nodoc: module ActionController # :nodoc: # = Macro test helpers for your controllers # # By using the macro helpers you can quickly and easily create concise and easy to read test suites. # # This code segment: # context "on GET to :show for first record" do # setup do # get :show, :id => 1 # end # # should_assign_to :user # should_respond_with :success # should_render_template :show # should_not_set_the_flash # # should "do something else really cool" do # assert_equal 1, assigns(:user).id # end # end # # Would produce 5 tests for the +show+ action module Macros include Matchers # Macro that creates a test asserting that the flash contains the given value. # val can be a String, a Regex, or nil (indicating that the flash should not be set) # # Example: # # should_set_the_flash_to "Thank you for placing this order." # should_set_the_flash_to /created/i # should_set_the_flash_to nil def should_set_the_flash_to(val) matcher = set_the_flash.to(val) if val should matcher.description do assert_accepts matcher, @controller end else should "not #{matcher.description}" do assert_rejects matcher, @controller end end end # Macro that creates a test asserting that the flash is empty. Same as # @should_set_the_flash_to nil@ def should_not_set_the_flash should_set_the_flash_to nil end # Macro that creates a test asserting that filter_parameter_logging # is set for the specified keys # # Example: # # should_filter_params :password, :ssn def should_filter_params(*keys) keys.each do |key| matcher = filter_param(key) should matcher.description do assert_accepts matcher, @controller end end end # Macro that creates a test asserting that the controller assigned to # each of the named instance variable(s). # # Options: # * :class - The expected class of the instance variable being checked. # * :equals - A string which is evaluated and compared for equality with # the instance variable being checked. # # Example: # # should_assign_to :user, :posts # should_assign_to :user, :class => User # should_assign_to(:user) { @user } def should_assign_to(*names, &block) opts = names.extract_options! if opts[:equals] warn "[DEPRECATION] should_assign_to :var, :equals => 'val' " << "is deprecated. Use should_assign_to(:var) { 'val' } instead." end names.each do |name| matcher = assign_to(name).with_kind_of(opts[:class]) test_name = matcher.description test_name << " which is equal to #{opts[:equals]}" if opts[:equals] should test_name do if opts[:equals] instantiate_variables_from_assigns do expected_value = eval(opts[:equals], self.send(:binding), __FILE__, __LINE__) matcher = matcher.with(expected_value) end elsif block expected_value = instance_eval(&block) matcher = matcher.with(expected_value) end assert_accepts matcher, @controller end end end # Macro that creates a test asserting that the controller did not assign to # any of the named instance variable(s). # # Example: # # should_not_assign_to :user, :posts def should_not_assign_to(*names) names.each do |name| matcher = assign_to(name) should "not #{matcher.description}" do assert_rejects matcher, @controller end end end # Macro that creates a test asserting that the controller responded with a 'response' status code. # Example: # # should_respond_with :success def should_respond_with(response) should "respond with #{response}" do matcher = respond_with(response) assert_accepts matcher, @controller end end # Macro that creates a test asserting that the response content type was 'content_type'. # Example: # # should_respond_with_content_type 'application/rss+xml' # should_respond_with_content_type :rss # should_respond_with_content_type /rss/ def should_respond_with_content_type(content_type) should "respond with content type of #{content_type}" do matcher = respond_with_content_type(content_type) assert_accepts matcher, @controller end end # Macro that creates a test asserting that a value returned from the session is correct. # The given string is evaled to produce the resulting redirect path. All of the instance variables # set by the controller are available to the evaled string. # Example: # # should_set_session(:user_id) { '@user.id' } # should_set_session(:message) { "Free stuff" } def should_set_session(key, expected = nil, &block) matcher = set_session(key) if expected warn "[DEPRECATION] should_set_session :key, 'val' is deprecated. " << "Use should_set_session(:key) { 'val' } instead." end should matcher.description do if expected instantiate_variables_from_assigns do expected_value = eval(expected, self.send(:binding), __FILE__, __LINE__) matcher = matcher.to(expected_value) end else expected_value = instance_eval(&block) matcher = matcher.to(expected_value) end assert_accepts matcher, @controller end end # Deprecated. See should_set_session def should_return_from_session(key, expected) warn "[DEPRECATION] should_require_attributes is deprecated. " << "Use should_set_session instead." should_set_session(key, expected) end # Macro that creates a test asserting that the controller rendered the given template. # Example: # # should_render_template :new def should_render_template(template) should "render template #{template.inspect}" do assert_template template.to_s end end # Macro that creates a test asserting that the controller rendered with the given layout. # Example: # # should_render_with_layout 'special' def should_render_with_layout(expected_layout = 'application') matcher = render_with_layout(expected_layout) if expected_layout should matcher.description do assert_accepts matcher, @controller end else should "render without layout" do assert_rejects matcher, @controller end end end # Macro that creates a test asserting that the controller rendered without a layout. # Same as @should_render_with_layout false@ def should_render_without_layout should_render_with_layout nil end # Macro that creates a test asserting that the controller returned a redirect to the given path. # The given string is evaled to produce the resulting redirect path. All of the instance variables # set by the controller are available to the evaled string. # Example: # # should_redirect_to("the user's profile") { user_url(@user) } def should_redirect_to(description, &block) unless block warn "[DEPRECATION] should_redirect_to without a block is " << "deprecated. Use should_redirect_to('somewhere') { } instead." end should "redirect to #{description}" do if block url = instance_eval(&block) else instantiate_variables_from_assigns do url = eval(description, self.send(:binding), __FILE__, __LINE__) end end assert_redirected_to url end end # Macro that creates a routing test. It tries to use the given HTTP # +method+ on the given +path+, and asserts that it routes to the # given +options+. # # If you don't specify a :controller, it will try to guess the controller # based on the current test. # # +to_param+ is called on the +options+ given. # # Examples: # # should_route :get, "/posts", :controller => :posts, :action => :index # should_route :get, "/posts/new", :action => :new # should_route :post, "/posts", :action => :create # should_route :get, "/posts/1", :action => :show, :id => 1 # should_route :edit, "/posts/1", :action => :show, :id => 1 # should_route :put, "/posts/1", :action => :update, :id => 1 # should_route :delete, "/posts/1", :action => :destroy, :id => 1 # should_route :get, "/users/1/posts/1", # :action => :show, :id => 1, :user_id => 1 # def should_route(method, path, options) unless options[:controller] options[:controller] = self.name.gsub(/ControllerTest$/, '').tableize end matcher = route(method, path).to(options) should matcher.description do assert_accepts matcher.in_context(self), self end end end end end