Musannif Zahir
Musannif Zahir

Reputation: 3029

Getting duplicate items when mocking using RSpec and Factory Girl

When using factory girl and RSpec to test my controller, I'm getting the error:

Failures:

  1) ProductsController Handling POST for create should redirect to products after success
     Failure/Error: response.should redirect_to(products_path)
        Expected response to be a <:redirect>, but was <200>

Based on the log file, it looks like the object already exists in the DB (and doesn't meet the redirect clause in the controller), which raises two questions:

  1. Why aren't the calls getting mocked/stubbed?
  2. What would a workaround be if I wanted to hit the DB? Using @params = FactoryGirl.build(:product).attributes.except(:id, :created_at, :updated_at) doesn't work either.

Any help would be awesome. Thank you.

test.log:

Processing by ProductsController#create as HTML
Parameters: {"product"=>{"name"=>"Fruit Mania", "description"=>"The mania that is taking over the planet!", "price"=>"9.99", "inventory"=>"1", "product_image"=>"#<File:0x00
User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Product Exists (0.1ms)  SELECT 1 AS one FROM "products" WHERE LOWER("products"."name") = LOWER('Fruit Mania') LIMIT 1

products_controller_spec.rb:

describe ProductsController, "Handling POST for create" do
  def sign_in_user
    user = FactoryGirl.create(:user)
    sign_in user, @user
  end

  def do_post
    post :create, :product => @params
  end

  before do
    @product = mock_model(Product, :save=> true)
    Product.stub!(:create).and_return(@product)
    @params = FactoryGirl.attributes_for(:product)
  end

  it "should redirect to products after success" do 
     sign_in_user
     do_post
     pending "need to figure out why redirect fails"
     response.should redirect_to(products_path)
  end
end

Factory:

FactoryGirl.define do
  factory :product do
    name "Fruit Mania"
    description "The mania that is taking over the planet!"
    price "9.99"
    inventory 1
    product_image { File.open(File.join(Rails.root, 'spec', 'support', 'uploads', '1000x450.jpeg')) }
  end
end

Controller:

 def create
    @product = Product.new(params[:product])
    if [email protected]?
      flash.now[:error] = ("The product was not added: <br><li>" + @product.errors.full_messages.join("<li>")).html_safe
      render "new"
    else
      if @product.save
        redirect_to products_path, :notice => "Product created successfully!"
      else
        flash.now[:error] = ("There were issues adding the product to the database. Please retry")
        render "new"
      end
    end
  end

Upvotes: 1

Views: 1699

Answers (1)

Geoff
Geoff

Reputation: 2228

I can tell you why the database call is happening in the first place.

You should replace this line:

Product.stub!(:create).and_return(@product)

with:

Product.any_instance.stub!(:save).and_return(true)

You are stubbing the class method Product::create, but you really need to stub the instance method Product#save.

I suppose the following would also work:

Product.stub!(:new).and_return(@product)
@product.stub(:save).and_return(true)

Because of the failure to save you go through the render path which gives an HTTP response of 200, not the 302 you were expecting.

As to why it's hitting the database, I'm afraid I can't say why the model already exists from the given context. If you only run this single test, does it work? Try checking out this question for more ideas:

Why isn't factory_girl operating transactionally for me? - rows remain in database after tests

I hope that helps.

Upvotes: 1

Related Questions