user2012677
user2012677

Reputation: 5775

rspec @variable returning nil

I have an issue with my @attributes variable. I would like it to be accessible to keep my code dry, but currently, I have to restate the variable and set it to "values" to get my rspec test to work. What is a better way to do this without duplicating the values.

ref: Unexpected nil variable in RSpec

Shows that it is not accessible in describe, but there needs be another solution. When would "specify" be appropriate? I have not used it.

describe "When one field is missing invalid " do 
    before(:each) do 
        @user = create(:user)
        @attributes = {"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}
    end
  values = {"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}
  values.keys.each do |f|
    p = values.except(f) 
    it "returns invalid when #{f.to_s} is missing" do 
              cr = CarRegistration::Vehicle.new(@user, p)
        cr.valid?
    end
  end
end

Update based on comments: I would also like to use the values array hash in other tests. If I put it in the loop as stated, I would still have to repeat it in other places. Any other recommendations?

Update: I tried using let(),

  describe "When one field is missing" do

        let(:user) {Factorybot.create(:user)}
        let(:attributes) = {{"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}}

      attributes do |f|
        p = attributes.except(f) 
        it "returns invalid when #{f.to_s} is missing" do 
                  cr = CarRegistration::Vehicle.new(user, p)
            cr.valid?
        end
      end
  end

but get the following error.

attributes is not available on an example group (e.g. a describe or context block). It is only available from within individual examples (e.g. it blocks) or from constructs that run in the scope of an example (e.g. before, let, etc).

Upvotes: 0

Views: 1166

Answers (3)

BenKoshy
BenKoshy

Reputation: 35741

Using let

describe "When one field is missing" do
  let(:user) {Factorybot.create(:user)}
  let(:attributes) = {{"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}}
  ## The variables are used INSIDE the it block.
  it "returns invalid when a key is missing" do
    attributes do |f|
      p = attributes.except(f)
      cr = CarRegistration::Vehicle.new(user, p)
      expect(cr.valid?).to eq(true)  # are you testing the expectation? Added this line.    
    end
  end
end

Personally I don't like writing test (like the above) which could fail for multiple reasons. Sergio is correct. But if you want to use let you have to make use of it from WITHIN the it block - this example shows that.

Upvotes: 0

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230561

In either of your snippets, you don't need attributes inside of your specs. It is data to generate specs. As such, it must live one level above.

describe "When one field is missing" do

  let(:user) { Factorybot.create(:user) }

  attributes = { "has_car" => "true", "has_truck" => "true", "has_boat" => "true", "color" => "blue value", "size" => "large value" }

  attributes do |f|
    p = attributes.except(f)
    it "returns invalid when #{f.to_s} is missing" do
      cr = CarRegistration::Vehicle.new(user, p)
      cr.valid?
    end
  end
end

Upvotes: 1

smathy
smathy

Reputation: 27971

As you seem to have recognized, based on the other SO post you linked to, you can't refer to your instance variables out in your describe block. Just set it as a local variable as you've done.

Upvotes: 0

Related Questions