steel
steel

Reputation: 12580

Rspec, Rails - unable to account for controller variable in testing

I am new to testing. I don't seem to be able to account for a variable in my controller in my rspec tests. How can I account for the needed variable in my tests and make these tests pass?

Current test:

describe "POST create action" do
    let(:trip)              { create(:trip)}
    let(:trip_date)         { create(:trip_date) }
    let(:buyer)             { create(:buyer) }
    let(:company)           { create(:company) }
    let(:order_item)        { attributes_for(:order_item, trip_date_id: trip_date.id, buyer_id: buyer.id, company_id: company.id) }
    let(:bad_order_item)    { attributes_for(:bad_order_item, trip_date_id: trip_date.id, buyer_id: buyer.id, company_id: company.id) }

    context "given valid order item attributes" do
        it "creates a new order item" do
            expect{ post :create, order_item: order_item, trip_id: trip.id }.to change(OrderItem, :count).by(1)
        end
    end
end

This is my error message:

OrderItemsController POST create with valid attributes creates a new order item
Failure/Error: expect{ post :create, order_item: FactoryGirl.attributes_for(:order_item) }.to change(OrderItem,:count).by(1)
NoMethodError:
       undefined method `company_id' for nil:NilClass
 # ./app/controllers/order_items_controller.rb:29:in `create'
 # ./spec/controllers/order_items_controller_spec.rb:47:in `block (4 levels) in <top (required)>'

Error references line 29 of my order_items_controller.rb:

line 25:    def create
line 26:        @trip = Trip.friendly.find_by_id(params[:trip_id)
line 27:        @order_item = OrderItem.new(order_item_params)
line 28:        @order_item.buyer_id            = current_user.id
line 29:        @order_item.company_id          = @trip.company_id
line 30:        @order_item.first_person_cost   = @trip.first_person_cost
line 31:        @order_item.second_person_cost  = @trip.second_person_cost

line 32:        if @order_item.save
line 33:            redirect_to cart_path(current_user), notice: 'New order item created.'
line 34:        else
line 35:            render 'new', notice: 'Unable to create new order item.'
line 36:        end    
line 37:    end  

The only callback affecting this controller action confirms the user is signed in before the action begins. If they are not, they are redirected to the sign in page.

I have also tried: let(:trip) { create(:trip)} let(:trip_date) { create(:trip_date) } let(:buyer) { create(:buyer) } let(:company) { create(:company) } let(:order_item) { attributes_for(:order_item, trip_date_id: trip_date.id, buyer_id: buyer.id, company_id: company.id) } let(:bad_order_item) { attributes_for(:bad_order_item, trip_date_id: trip_date.id, buyer_id: buyer.id, company_id: company.id) }

describe "POST create" do
    let(:trip)     {create(:trip) }
    context "with valid attributes" do
        it "creates a new order item" do
          Trip.stub_chain(:friendly, :find_by_id).and_return(trip)
          expect{ post :create, order_item: order_item }.to change(OrderItem,:count).by(1)
          end
      end
    end
end

Resulting in an error of:

1) OrderItemsController POST create action given valid order item attributes creates a new order item
    Failure/Error: let(:trip)               { create(:trip)}
    Double received unexpected message :where with (no args)
        # ./spec/controllers/order_items_controller_spec.rb:34:in `block (3 levels) in <top (required)>'

Additionally, documentation on specific Rpsec code can be hard to find. I'd especially appreciate being pointed in the right direction toward the docs for rspec's 'post' method.

Thanks in advance.

Upvotes: 0

Views: 296

Answers (1)

gotva
gotva

Reputation: 5998

You can't assign some variables in spec and get them in controller!

There are several ways how to test it

1 Controller finds record in DB - so create it that Trip.friendly.find_by_id(params[:trip_id) return it

describe "POST create" do
  let(:trip) { FactoryGirl.create(:trip) }

  context "with valid attributes" do
    it "creates a new order item" do
      order_item = FactoryGirl.attributes_for(:order_item)
      expect{ post :create, order_item: order_item, trip_id: trip.id }.to change(OrderItem,:count).by(1)
    end
  end
end 

2 Use stub (or stub_chain)

describe "POST create" do
  let(:trip) { FactoryGirl.create(:trip) }
  context "with valid attributes" do
    it "creates a new order item" do
      Trip.stub_chain(:friendly, :find_by_id).and_return(trip)
      order_item = FactoryGirl.attributes_for(:order_item)
      expect{ post :create, order_item: order_item }.to change(OrderItem,:count).by(1)
    end
  end
end

PS As for me first variant is better, because controller really accept params[:trip_id] so it is better to pass it.

Upvotes: 1

Related Questions