Hiroaki Machida
Hiroaki Machida

Reputation: 1090

Rspec fails for Name has already been taken

I'm trying to make rspec testcases. But, Rspec fails for Name has already been taken.

It seems "let" evalulated each time "product" called.

How can I fix it?

Console

./spec/models/spree/product_decorator_spec.rb:31:in `block (4 levels) in <top (required)>'

ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
./spec/models/spree/product_decorator_spec.rb:6:in `block (3 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:20:in `block (4 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:23:in `block (4 levels) in <top (required)>'

ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
./spec/models/spree/product_decorator_spec.rb:6:in `block (3 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:12:in `block (4 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:15:in `block (4 levels) in <top (required)>'

product_decorator_spec.rb

require 'spec_helper'

describe Spree::Product do

  context '#create' do
    let(:us) { create(:zone, name: "US") }
    let(:china) { create(:zone, name: "China") }
    let(:japan) { create(:zone, name: "Japan") }


    context "when a product has no ng zone" do
      let(:product) { create(:product, zones: [us, china, japan]) }

      it "should get ng_zones correctly" do
        product.ng_zones.should match_array []
      end
    end

    context "when a product has one ng zone" do
      let(:product) { create(:product, zones: [us, china]) }

      it "should get ng_zones correctly" do
        product.ng_zones.should match_array ["Japan"]
      end
    end

    context "when a product has two ng zone" do
      let(:product) { create(:product, zones: [us]) }

      it "should get ng_zones correctly" do
        product.ng_zones.should match_array ["China", "Japan"]
      end
    end
  end
end

Upvotes: 0

Views: 1435

Answers (2)

Kleber S.
Kleber S.

Reputation: 8240

You are testing the Product#create so, you won't need to really create zones for this particular test.

Instead you could just use build_stubbed method.

let(:us) { build_stubbed(:zone, name: "US") }
let(:china) { build_stubbed(:zone, name: "China") }
let(:japan) { build_stubbed(:zone, name: "Japan") }

This way, it's creation process are not going to rely on the database and also the validations for the Zone model and you have an awesome performance boost since you are not hitting the db for for each single test.

You can read about build_stubbed here.

Please let me know if it helps you somehow. ;)

Upvotes: 0

Bert Goethals
Bert Goethals

Reputation: 7887

The body of the let is evaluated with every it block. I'm assuming you have a uniqueness constraint on your Zone classes.

You have 2 possibilities

  • Either build the variables in a before(:all) block and assign them to something like @us
  • Clean up your database after(:each)

Upvotes: 0

Related Questions