I recently developed this pattern in order to clean up my tests and shoulda contexts. The initial inspiration was that I needed a global parameter to be passed all the time, for security purposes. Then I figured I could also use it with my nested contexts, to get cleaner tests. The special sauce is the overloading of get and post methods, which merges with the @params instance variable. I am also using the @action variable to hold a proc, so that I can keep my tests DRYer. Please let me know what you think in the comments!
PS also notice the use of the slick new should_change macro, which assists me in keeping to one assert per should (and not having to put assert_difference blocks in my setups). (thanks to Ryan McGeary! lighthouse ticket, github commit )
require File.dirname(__FILE__) + '/../test_helper'
require 'events_controller'
require "test/unit"
# Re-raise errors caught by the controller.
class EventsController; def rescue_action(e) raise e end; end
class EventsControllerTest < Test::Unit::TestCase
## special sauce for global parameters
[ :get, :post ].each do |overloaded_method|
define_method overloaded_method do |*args|
action,params,extras = *args
super action, @params.merge( params || {} ), *extras
end
end
def setup
@controller = EventsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@event = create_event
@user = User.first || create_user
@params = {:security_token => 'abc123' } # add any global params you need here
end
context "A POST to :rsvp" do
setup do
@action = lambda{ post :rsvp, :id => @event.id }
end
context "with no RSVP existing in DB" do
setup do
if rsvp = @user.rsvps.find_by_event_id( @event.id )
rsvp.destroy
end
end
%w[ attending not_attending maybe_attending ].each do |status|
context "with :status = '#{status}'" do
setup do
@params[:status] = status
@action.call
end
should_respond_with :success
should_change "Rsvp.count", :by => 1
should "create the proper Rsvp object" do
assert Rsvp.find_by_user_id_and_event_id_and_status( @user.id, @event.id, status )
end
end
end
end
context "with an existing RSVP in DB" do
setup do
@rsvp = create_rsvp( :event_id => @event.id, :user_id => @user.id, :status => 'not_attending' )
end
context "" do # HACK: this blank context is needed to separate the create_rsvp from the should_not_change call (without it, the count would change by 1)
setup do
@params[:status] = 'attending'
@action.call
end
should_respond_with :success
should_not_change "Rsvp.count"
should "update the status" do
assert_equal @params[:status], @rsvp.reload.status
end
end
end
end
context "a POST to :create" do
setup do
@action = lambda{ post :create }
end
context "when the form fields are blank" do
setup do
@params[:event] = {}
end
should_respond_with :success
should_render_template :edit
should "show validation errors" do
assert_select '.fieldWithErrors'
end
end
context "with valid params and :publish => true" do
setup do
@params[:event] = { :title => "Some New Event", :starts_on => Date.today, :all_day => true, :ends_on => Date.today+1 }
@params[:publish] = true
@action.call
end
should "show up in event index" do
@event = Event.find_by_title("Some New Event")
assert Event.visible.include?( @event )
end
context "and no end date" do
setup do
@params[:ends_on] = nil
@action.call
end
should_respond_with :success
should_render_template :show
should "show up in event index" do
@event = Event.find_by_title("Some New Event")
assert Event.visible.include?( @event )
end
end
end
end
end


Sorry, comments are closed for this article.