Resetting cancan instance variables through rspec

Recently I have faced some problems regarding rspec with cancan. The specs what I have written are working on development but when I run my test suite it fails. Here is the scenario:

User should be able to create multiple vouchers for a booking

Relationships:

# app/models/voucher.rb
    belongs_to : project
# app/models/booking.rb
    has_many :vouchers

Initially my controller and spec code looks as follows:

# app/controllers/vouchers_controller.rb
     before_filter :load_booking

     def create
        @voucher = @booking.vouchers.new(params[:voucher])
        @voucher.save
     end

     private

     def load_booking
        @booking = Booking.find(params[:booking_id])
     end
  # spec/controllers/vouchers_controller_spec.rb
  
    it "user should be able to create multiple vouchers for a booking" do
      booking = create(:company_staff_booking)
      user = create(:org_admin) 
      sign_in user 
      voucher_params.merge!(remarks_attributes: {"0" =>{description: "Adding new remarks", created_by_id: user.id}})
      post :create, voucher: voucher_params, booking_id: booking.id
      reset_instance_variables
      voucher_params.merge!(instrument_no: '0987654321')
      post :create, voucher: voucher_params, booking_id: booking.id

      expect(booking.reload.vouchers.count).to eq(2)
    end 

The above specification has passed initially. Later my developer has changed the code to as follows:

  # app/controllers/vouchers_controller.rb

  load_and_authorize_resource :booking
  load_and_authorize_resource :voucher, :through => :booking

  def create
     @voucher.save
  end

After this change my specs starts failing.

After few unsuccessfull debugging attempts I found that issue neither lies with my specs nor with my code. It lies with cancan gem.

When I sending request initially it loads booking object into @booking instance variable and then I build voucher object from booking object explicitly.

   load_and_authorize_resource :booking 

Later instead of building voucher object explicity I am building voucher object through cancan as follows.

   load_and_authorize_resource :voucher, through: booking 

If you are loading objects through cancan, it checks whether there is a variable exists or not in memory. If it exists it uses same object instead of creating new voucher object. So when I sends my second request from specs instead of creating second object it loads old object. As a result my specs are failing. To solve this problem I deleting instance variables explicitly in-between the requests. Finally my spec code looks as follows:

   # spec/controllers/vouchers_controller_spec.rb

   it "user should be able to create multiple vouchers for a booking" do
      booking = create(:company_staff_booking)
      user = create(:org_admin) 
      sign_in user 
      voucher_params.merge!(remarks_attributes: {"0" =>{description: "Adding new remarks", created_by_id: user.id}})
      post :create, voucher: voucher_params, booking_id: booking.id
      
      @controller.instance_variable_set(:@voucher, nil)
      @controller.instance_variable_set(:@booking, nil)

      voucher_params.merge!(instrument_no: '0987654321')
      post :create, voucher: voucher_params, booking_id: booking.id

      expect(booking.reload.vouchers.count).to eq(2)
    end 

Hope it clears cancan concepts and how to handle those scenarios in specs. Any suggestions would be welcome.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s