[rspec-users] Can some one please explain why one of those two examples fails?

Zach Dennis zach.dennis at gmail.com
Thu Mar 15 14:54:04 UTC 2012


On Thu, Mar 15, 2012 at 8:46 AM, Mohamad El-Husseini
<husseini.mel at gmail.com> wrote:
> Thanks, Mike. I appreciate the explanation. It's tricky knowing what runs
> when, and what variable is in what scope. It seems like "code smell" to add
> an instance variable to the before block.
>
> I don't understand what advantage one approach has over the other. What
> would you use, the first, that was broken, or the second?

I am assuming you're asking about what the advantage of using #subject
vs. just #user is? If so, here are some advantages of using #subject
IMO:

RSpec will provide an implicit #subject already and in many cases you
don't need to construct a new one. This gets rid of an unnecessary
line in those instances. Here's an example from the docs:

    describe Array do
      it "should be empty when first created" do
        subject.should be_empty
      end
    end

   See https://www.relishapp.com/rspec/rspec-core/docs/subject/implicitly-defined-subject
for more info.

Rspec's one liner syntax uses #subject, straight out of the docs is a
great example:

    describe Array do
      describe "with 3 items" do
        subject { [1,2,3] }
        it { should_not be_empty }
      end
    end

   See https://www.relishapp.com/rspec/rspec-core/v/2-8/docs/subject/implicit-receiver
for more information.
   Also see https://www.relishapp.com/rspec/rspec-core/v/2-8/docs/subject/attribute-of-subject

Since #subject is an RSpec convention when writing shared example
groups they are often set up to expect subject to be defined by the
including example group. Here's an example practically from the docs:

    shared_examples "a measurable object" do |value|
      it "should return #{value} from #length" do
        subject.send(:length).should ==value
      end
    end

    describe Array, "with 3 items" do
      subject { [1, 2, 3] }
      it_should_behave_like "a measurable object", 3
    end

    See https://www.relishapp.com/rspec/rspec-core/v/2-8/docs/example-groups/shared-examples
for more info.

And since #subject is an RSpec convention, it always implies the
"thing" under test. So, if you want to know what you're testing you
can either do a visual scan for a subject block or simply look at the
class/module being described.

Those are the reasons I find #subject to have advantage. By
themselves, none of these are huge reasons, but combined, and over
time, I have found relying on convention and getting a few extra
niceties in my specs outweighs going against the convention and trying
to reinvent conventions when I hit things like one-liners or shared
examples/contexts.

My 2 cents,

Zach


>
>
> On Tuesday, March 13, 2012 9:24:03 PM UTC-3, Mike Mazur wrote:
>>
>> Hi,
>>
>> On Wed, Mar 14, 2012 at 07:55, Mohamad El-Husseini
>> <husseini.mel at gmail.com> wrote:
>> > The following are what I believe two ways of doing the same thing. Only
>> > the
>> > first example fails, while the latter passes.
>>
>> In your failing example:
>>
>>    context "generates a unique password_reset_token each time" do
>>       let(:user) { FactoryGirl.create(:user) }
>>       before do
>>         user.send_password_reset
>>         last_token = user.password_reset_token
>>         user.send_password_reset
>>       end
>>       its(:password_reset_token) { should_not == last_token }
>>     end
>>
>> The `last_token` variable is in scope in the before block, but not in
>> the its block. You can fix this by changing it to an instance
>> variable:
>>
>>    context "generates a unique password_reset_token each time" do
>>       let(:user) { FactoryGirl.create(:user) }
>>       before do
>>         user.send_password_reset
>>         @last_token = user.password_reset_token
>>         user.send_password_reset
>>       end
>>       its(:password_reset_token) { should_not == @last_token }
>>     end
>>
>> Another gotcha is: what is the its expression making assertions on?
>> The its method requires a subject to be defined.
>>
>> RSpec defines an implicit subject based on the described class. I
>> imagine at the top of your .spec file you have something like:
>>
>>   describe User do
>>     # stuff
>>   end
>>
>> RSpec will generate a subject by calling `User.new`.
>> `password_reset_token` is then called on this new user instance in the
>> its block. You most certainly wanted to call `password_reset_token` on
>> the user object defined by the `let` statement.
>>
>> So I think this should do the trick:
>>
>>    context "generates a unique password_reset_token each time" do
>>       let(:user) { FactoryGirl.create(:user) }
>>       subject { user }
>>       before do
>>         user.send_password_reset
>>         @last_token = user.password_reset_token
>>         user.send_password_reset
>>       end
>>       its(:password_reset_token) { should_not == @last_token }
>>     end
>>
>> You can read more about RSpec's subject here:
>>
>>   https://www.relishapp.com/rspec/rspec-core/docs/subject
>>
>> Hope that helps,
>> Mike
>> _______________________________________________
>> rspec-users mailing list
>> rspec-users at rubyforge.org
>> http://rubyforge.org/mailman/listinfo/rspec-users
>
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users



-- 

--
@zachdennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com


More information about the rspec-users mailing list