Having trouble with one-liner and change matcher

566 views
Skip to first unread message

Greg Lappen

unread,
Mar 10, 2015, 10:49:18 PM3/10/15
to rs...@googlegroups.com
Hi all, long time-user here, but first time poster... I've been a fan of rspec for about 6 years now.

Anyway, I'm writing because I'm making some specs and trying to use the one-liner syntax to keep it brief.  It works, except for when I try to use it with the change matcher.  It's a simple controller spec I'm doing:

    describe "GET #index" do
      subject { get :index }

      it { should be_success }

      # attempt 1
      it { is_expected.to change{assigns(:assets)}.from(nil) }

      # attempt 2
      it { should change{assigns(:assets)}.from(nil) }

      # attempt 3
      it "should assign @asset" do
        expect{subject}.to change {assigns(:assets)}.from(nil)
      end
    end

Both attempt 1 and 2 above fail with the message "expected result to have changed from nil, but was not given a block".  Attempt 3 succeeds.

Can anyone explain what's going on here?

Thanks so much!

Greg



Jon Rowe

unread,
Mar 10, 2015, 10:51:04 PM3/10/15
to rs...@googlegroups.com
The `change` matcher requires a block to execute so it can evaluate before / after, but the one liner syntax doesn’t support that, so you need to use the third form, to improve readability here you could use the named subject, `subject(:index) { get :index }` and thus `expect { index }.to change { assigns(:assets) }.from(nil)`

Jon Rowe
---------------------------

--
You received this message because you are subscribed to the Google Groups "rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rspec+un...@googlegroups.com.
To post to this group, send email to rs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rspec/3549afe2-70b2-4cd2-8fd1-6f31ad42b13f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Myron Marston

unread,
Mar 10, 2015, 11:38:02 PM3/10/15
to rs...@googlegroups.com

On Tue, Mar 10, 2015 at 7:50 PM, Jon Rowe <ma...@jonrowe.co.uk> wrote:

The `change` matcher requires a block to execute so it can evaluate before / after, but the one liner syntax doesn’t support that, so you need to use the third form, to improve readability here you could use the named subject, `subject(:index) { get :index }` and thus `expect { index }.to change { assigns(:assets) }.from(nil)`

Jon Rowe
---------------------------

Technically, the one liner syntax does support blocks, but you have to make your subject return a proc or lambda:

 describe "GET #index" do
  subject { lambda { get :index } }

  # either of these work

  it { is_expected.to change { assigns(:assets) }.from(nil
) }
  it { should change { assigns(:assets) }.from(nil) }
end

I don’t think that’s a good use of RSpec’s API, though; the one-liner syntax has valid uses but I don’t think this is a good case for it.

Actually, in this case I’m not even sure you need to use the change matcher; aren’t all assigns initially nil before the controller action has been called? (It’s been a long time since I’ve done any Rails but that’s what I remember). If so, the change matcher seems unnecessary here; you could just use a simpler specify { expect(assigns(:assets)).not_to be_nil }.

More generally, the reason the change matcher requires a block, proc or lambda is because it observes a side effect (e.g. the mutation of some state) rather than being a simple matcher applied to a value. The other block matchers (raise_error, throw_symbol, yield_args, etc) similarly are expectations about a side effect from running a bit of code rather than being about the return value of an expression.

HTH,
Myron

Reply all
Reply to author
Forward
0 new messages