I just cooked up a macro where I needed to access an instance variable from the setup blocks. However the scope of should statements are at the class level, so we don’t have access to instance variables from there (only class variables). I found the solution inside the code for should_redirect_to, which evals the object in the binding of the should block.
class ProjectsControllerTest < ActionController::TestCase
def setup
super
@project = create_project
end
%w[ client developer ].each do |role|
context "A #{role} user" do
setup do
login_as( user = create_user )
@project.create_role_for!( user, role )
end
should_not_allow :destroy, "@project"
should_not_allow :edit, "@project"
end
end
end
This next file goes in test/shoulda_macros/*.rb, which will be auto-loaded:
Test::Unit::TestCase.class_eval do
def self.should_not_allow action, object, msg=nil
msg ||= "a #{object.class.to_s.downcase}"
should "not be able to #{action} #{msg}" do
object = eval(object, self.send(:binding), __FILE__, __LINE__)
get action, :id => object.id
assert_response 404
end
end
def self.should_allow action, object, msg=nil
msg ||= "a #{object.class.to_s.downcase}"
should "be able to #{action} #{msg}" do
object = eval(object, self.send(:binding), __FILE__, __LINE__)
get action, :id => object.id
assert_response :success
end
end
end


October 29th, 2008 at 12:18 PM
Cheers David, just what I was looking for. There seems to be one issue with it though, the default msg always reports object as a string as it hasn't been evaled yet.
Oh and for anyone else trying to use these macros, make sure you set:
config.actioncontroller.considerallrequestslocal = false
in config/environments/test.rb otherwise you'll probably just get an uncaught ActiveRecord::RecordNotFound when calling shouldnotallow.