Search code examples
ruby-on-railsrspecruby-debug

RSpec - Values not being set to user attributes properly


I've got a test I'm trying to debug and I've noticed that the values are not being set to the user attributes properly. When I run p user.height_feet or p user.height_inches from the debug console, I get nil, when instead I expect the them to return 1 and 8 respectively in the first iteration. p invalid_height.first and p invalid_height.second return 1 and 8 properly, however.

Here is the code:

describe "when height is invalid" do
  invalid_height = [[1, 8], [8, 2], [5, 13], ['text', 'text'], ['text', 11], [5, 'text'], ['', '']]
  invalid_height.each do |invalid_height|
    before do
      user.height_feet = invalid_height.first
      user.height_inches = invalid_height.second
    end

    it "should not be valid" do
      debugger
      user.should_not be_valid
    end
  end
end

And the output at the debug terminal:

(rdb:1) p user.height_feet
nil
(rdb:1) p user.height_inches
nil
(rdb:1) p invalid_height.first
1
(rdb:1) p invalid_height.second
8

Someone in the #rubyonrails IRC channel suggested that it may be a scope issue and asked where my user is defined, saying that my before and it blocks may be referring to different users. I didn't think this should be an issue because I have other tests in the same spec file with both before and it blocks that run just fine. Thoughts?


Solution

  • You need to think what your code is doing.

    It goes through the each and creates a before and an it "should not be valid" but these are all eval-ed in the same scope.

    So you create a load of before blocks

    before do
      user.height_feet = 1
      user.height_inches = 8
    end
    
    before do
      user.height_feet = 8
      user.height_inches = 2
    end
    
    ...
    
    before do
      user.height_feet = ""
      user.height_inches = ""
    end
    

    And you create a load of it blocks

    it "should not be valid" do
      debugger
      user.should_not be_valid
    end
    
    it "should not be valid" do
      debugger
      user.should_not be_valid
    end
    
    ...
    
    it "should not be valid" do
      debugger
      user.should_not be_valid
    end
    

    So the result of all of your tests is basically just

    before do
      user.height_feet = ""
      user.height_inches = ""
    end
    
    it "should not be valid" do
      debugger
      user.should_not be_valid
    end
    

    Which I believe was not your intent.

    The obvious fix is to use a context block. This will seal each pair of statements into a context.

    [[1, 8], [8, 2], [5, 13], ['text', 'text'], ['text', 11], [5, 'text'], ['', '']
    ].each do |feet, inches|
      context "with an invalid height of #{feet} feet, #{inches} inches" do
    
        before do
          user.height_feet = feet
          user.height_inches = inches
        end
    
        it "should not be valid" do
          debugger
          user.should_not be_valid
        end
      end
    end