I've noticed examples of instance variable usage in RSpec unit tests etc.
Something like this:
it 'should update something' do
@user = user(:userone)
@attr = {
:name => 'this',
:phone => 'that'
}
put :update, :id => @user.id, :user => @attr
@user.reload
expect(response.status).to eq(200)
expect(@user.name).to eq('this')
expect(@user.phone).to eq('that')
end
Why not just use local variables if not setting the variable within a before :each
etc.?
The overall issue is that variables in tests (like variables in any program) should have the least scope possible. That makes the test easier for readers to understand, since it minimizes the amount of code they have to think about, and it often allows the runtime to be more efficient. So if an instance variable (or, in current RSpec, a let
variable) can be a local (without duplicating code or losing efficiency) it should be.
Back when the best available way to pass data from a before
block to an example (an it
block) was in an instance variable, I used to see specs like your example all the time. Sometimes they were written by inexperienced programmers who saw a lot of instance variables used in specs and unthinkingly imitated that. (I still see the same error in Rails controllers.) Sometimes they resulted from someone inlining a before block but not taking the trouble to change instance variables that no longer needed to be instance variables into locals.
These days RSpec provides let
. One reason let
is nicer than an instance variable is that uses of a let
variable look like locals, so you don't need to change them if you change a local to a let
variable or vice versa. However, I still often see the problem of test data which is used only in one example being defined in a let
variable when it could and should just be a local. Like overuse of instance variables, this sometimes comes from inexperienced programmers unthinkingly imitating examples, and sometimes from a failure to clean up after a change in a test makes a let
variable unnecessary.