0

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).

5
  • You can run the looping of the keys from INSIDE the it block, that way you won't need to duplicate @attributes with the 'values' variable. Commented Mar 3, 2019 at 7:52
  • @BKSpurgeon Any recommendations for reusing the keys outside of the bloc for other others too? Commented Mar 4, 2019 at 18:57
  • try using the let helper methods to allow reuse outside the block: relishapp.com/rspec/rspec-core/docs/helper-methods/let-and-let Commented Mar 5, 2019 at 3:17
  • @BKSpurgeon, I tried let and get the above error. Commented Mar 6, 2019 at 1:33
  • refer to my answer below or alternatively, follow sergio's answer. Commented Mar 6, 2019 at 2:57

3 Answers 3

1

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
Sign up to request clarification or add additional context in comments.

4 Comments

i accidentally edited this answer. please revert to original version. apologies.
@BKSpurgeon: no worries :) JFYI, this approach (loop over a data structure and create several it blocks) is somewhat widely used. Makes crystal clear failure logs. Unlike your version ;) (which would produce "expected false to be true")
@BKSpurgeon: good point about the spec missing an actual assertion! Didn't notice.
you are 100% correct i've added a note for OP's benefit.
0

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.